虚拟串口驱动 开发
敏捷的软件开发方法依赖于服务虚拟化,以赋予每个IT团队自治权。 这种方法消除了障碍,使自治团队可以继续开发活动,而不必等待任何人。 这样,团队就可以开始迭代/冲刺,从而开始集成测试。
自动化服务的工作方式
消费者可以通过已发布的端点使用任何自动化服务。 这意味着只有在线提供服务后,服务才能自动化。
任何希望利用可用的自动化服务的消费者都必须能够通过HTTP协议向该服务的端点发送请求。 这些服务中的某些服务在通过HTTP协议接收到请求后,将通过简单地发送一些数据进行响应。 其他服务可能会通过实际执行一些工作来响应通过HTTP协议收到的请求。 例如,服务可以创建资源(例如,创建订单),更新资源(更新订单)或删除资源(取消订单)。
所有这些活动都是通过HTTP协议触发的。 在最简单的情况下,服务使用者发起的动作是GET(例如HTTP GET)。 该请求可能带有一些查询值。 服务将使用这些值来缩小搜索范围(例如“搜索订单号12345并返回数据”)。
什么是服务虚拟化?
现在我们了解了自动化服务的工作原理,应该更容易理解如何对其进行虚拟化。 简而言之,可以模拟托管站点上发布的任何服务。 您可以插入模拟真实服务行为的伪造服务,而不是直接向服务提供商的端点发送HTTP请求。
从服务使用者的角度来看,无论是与真实服务还是虚假服务进行交互,都没有任何区别。 相互作用保持相同。
虚拟化一项服务
好吧,足够多的谈话,我会袖手旁观,并展示如何实际操作。 假设您的团队正在开始一个新项目,并以完整的用户故事形式接收需求:
验证用户
作为新应用
我想验证用户
因为我们要确保应用程序的适当安全性
验收标准
方案1: 新应用成功验证了用户身份
假设用户已导航到登录页面
并且用户已提交凭据
当新应用收到登录请求时
然后,新应用成功验证了用户身份
新的应用程序将显示响应消息“用户已成功登录”。
方案2: 新应用无法在首次尝试时对用户进行身份验证
假设用户已导航到登录页面
并且用户已提交凭据
当新应用收到登录请求时
然后,新应用无法成功验证用户身份
新应用会显示响应消息“登录错误。您还有2次尝试失败。”
方案3: 新应用无法在第二次尝试中对用户进行身份验证
假设用户已导航到登录页面
并且用户已提交凭据
当新应用收到登录请求时
然后,新应用无法成功验证用户身份
新应用会显示响应消息“登录不正确。您还有1次尝试机会。”
方案4: 新应用无法在第三次尝试时对用户进行身份验证
假设用户已导航到登录页面
并且用户已提交凭据
当新应用收到登录请求时
然后,新应用无法成功验证用户身份
新的应用程序将显示响应消息“登录错误。您再也没有尝试了。”
开始研究此用户故事时,要做的第一件事是创建所谓的“行走骨架”(对于本练习,我将使用标准的.Net Core平台以及我在之前的文章中讨论的xUnit.net ( 从这一个开始,再到另一个示例 )。有关如何安装,配置和运行所需工具的技术详细信息,请参考它们。
通过打开命令行并键入以下内容来创建行走骨架基础结构:
mkdir AuthenticateUser
然后移入AuthenticateUser文件夹:
cd AuthenticateUser
并创建一个单独的文件夹进行测试:
mkdir tests
移入tests文件夹( cd tests )并启动xUnit框架:
dotnet new xunit
现在将一个文件夹上移(回到AuthenticateUser )并创建app文件夹:
mkdir app
cd app
创建C#代码所需的支架:
dotnet new classlib
现在可以行走的骨架了! 打开您选择的编辑器并开始编码。
首先编写失败的测试
本着TDD的精神,从编写失败的测试开始(请参阅上一篇文章,以了解在使测试通过之前,查看失败的原因很重要):
using
System
;
using Xunit
;
using app
;
namespace tests
{
public
class UnitTest1
{
Authenticate auth
=
new Authenticate
(
)
;
[ Fact
]
public
void SuccessLogin
(
)
{
var given
=
"credentials"
;
var expected
=
"Successful login."
;
var actual
= auth
.
Login
( given
)
;
Assert
.
Equal
( expected, actual
)
;
}
}
}
此测试表明,如果有人在处理请求时向Authenticate组件的Login方法提供了一些凭据(即,秘密的用户名和密码),则预期会返回消息“ Successful login”。
当然,这是尚不存在的功能-SuccessLogin()模块中的实例化Authenticate模块尚未编写。 因此,您不妨继续尝试并首先编写所需的功能。 在app文件夹中创建一个新文件( Authenticate.cs ),并添加以下代码:
using
System
;
namespace app
{
public
class Authenticate
{
public
string Login
(
string credentials
)
{
return
"Not implemented"
;
}
}
}
现在,导航到tests文件夹并运行:
dotnet test
该测试失败,因为它期望获得“成功登录”输出,但获得了“未实现”输出。
第二天操作的复杂性增加
现在您已经创建了“幸福的道路”期望,并使其失败了,现在该着手实现使失败的测试通过的功能。 第二天,您参加站立会议并报告您已开始“认证用户”故事。 您让团队知道您已经为“快乐之路”创建了第一个失败的测试,而今天的计划是实施代码以使失败的测试通过。
您将说明打算首先创建一个包含用户名 , 密码和其他相关属性的用户表的意图。 但是Scrum 管理员打断了并解释说User模块正在由另一个团队处理。 重复维护用户是不好的做法,因为信息将很快失去同步。 因此,您无需构建User模块(该模块将包括身份验证逻辑),而是利用User团队正在使用的身份验证服务。
这是个好消息,因为它免除了您必须编写大量代码来实现User处理的麻烦。 底气十足,您热情洋溢地宣布,您将Swift完善一个功能,该功能将获取用户凭据并将其发送给用户团队已建立的服务。
las,当您得知用户团队尚未开始构建用户身份验证服务时,您的意图再次被压制。 他们仍在为积压工作分配用户故事。 令人沮丧的是,您辞职的事实是,您至少需要几天(如果不是几周?)就可以开始研究用户认证故事。
然后,Scrum 管理员说没有理由等待构建用户身份验证服务并将其部署到测试中。 您可以立即开始开发身份验证功能。 但是你该怎么做呢?
Scrum管理员提出了一个简单的建议:利用服务虚拟化。 由于用户模块的所有规范均已固化并签字,因此您将拥有坚实,稳定的合同来构建您的解决方案。 用户服务团队发布的合同规定,为了对用户进行身份验证,必须满足特定的期望:
- 希望验证用户身份的客户端应将HTTP POST请求发送到端点http://some-domain.com/api/v1/users/login 。
- 发送到上述端点的HTTP POST必须具有包含用户凭证(即用户名和密码)的JSON有效负载。
- 收到请求后,服务将尝试登录用户。如果用户名和密码与记录的信息匹配,则该服务将返回一个HTTP响应,其中包含状态码200,响应的正文包含消息“用户已成功登录”。在。”
因此,既然您知道合同的详细信息,就可以开始构建解决方案了。 这是连接到端点,发送HTTP POST请求并接收HTTP响应的代码:
using
System
;
using
System. Net
.
Http
;
using
System. Threading
.
Tasks
;
using
System. Collections
.
Generic
;
namespace app
{
public
class Authenticate
{
HttpClient client
=
new HttpClient
(
)
;
string endPoint
=
"http://some-domain.com/api/v1/users/login"
;
public
string Login
(
string credentials
)
{
Task
<
string
> response
= CheckLogin
( credentials
)
;
return response
.
Result
;
}
private
async Task
<
string
> CheckLogin
(
string credentials
)
{
var values
=
new Dictionary
<
string ,
string
>
{
{
"credentials" , credentials
}
}
;
var content
=
new FormUrlEncodedContent
( values
)
;
var response
=
await client
.
PostAsync
( endPoint, content
)
;
return
await response
.
Content
.
ReadAsStringAsync
(
)
;
}
}
}
由于http://some-domain.com不存在(尚未),因此该代码不起作用。 您现在停滞不前,等待其他团队最终构建和部署该服务吗?
并不是的。 服务虚拟化抢救! 让我们假装该服务已经存在并继续开发。
如何虚拟化服务
虚拟化用户身份验证服务的一种方法是编写一个新应用(新API)并在本地运行。 该API将镜像实际用户身份验证 API指定的合同,并且仅返回硬编码的存根数据(它将是伪造的服务)。
听起来是个好计划。 再次,团队在站立时退缩,质疑是否需要编写,构建,测试和部署全新的应用程序才能完成此虚假功能。 这种麻烦根本不值得,因为当您交付新的假应用时,其他团队可能已经准备好提供真正的服务。
所以你陷入了僵局。 看来您被迫等待依赖的实现。 您无法控制依赖关系; 您现在没有其他资源,只能按顺序工作。
没那么快! 有一个名为mountebank的出色新工具,非常适合虚拟化任何服务。 使用此工具,您可以快速站起本地服务器,该服务器侦听您指定的端口并接受订单。 要使其模拟服务,您只需告诉它要侦听的端口以及要处理的协议。 协议的选择是:
- HTTP
- HTTPS
- SMTP
- TCP协议
在这种情况下,您需要HTTP协议。 首先,安装mountebank-如果您的计算机上装有npm ,则只需在命令行中键入:
npm install -g mountebank
安装后,通过键入以下内容运行mountebank:
mb
在启动时,mountebank将显示:
现在您可以虚拟化HTTP服务了。 在这种情况下, 用户身份验证服务希望收到HTTP POST请求; 这是实现的代码如何发送HTTP POST请求的方法:
var response = await client.PostAsync ( endPoint , content ) ;
现在,您必须建立该endPoint 。 理想情况下,所有虚拟化服务都应在localhost服务器中进行支持,以确保快速执行集成测试。
为此,您需要配置imposter 。 冒名顶替者是一个简单的JSON集合,包含一个端口和一个协议的定义:
{
"port"
:
3001 ,
"protocol"
:
"http"
}
该冒名顶替者被配置为处理HTTP协议,并侦听端口3001上的传入请求。
仅侦听端口3001上的传入HTTP请求不会做很多事情。 一旦请求到达该端口,就需要告知mountebank如何处理该请求。 换句话说,您不仅要虚拟化特定端口上服务的可用性,还要虚拟化虚拟化服务响应请求的方式。
要实现该级别的服务虚拟化,您需要告诉mountebank如何配置存根。 每个存根包含两个组件:
- 谓词集合
- 预期回应的集合
谓词(有时称为匹配器)缩小了传入请求的范围。 例如,使用HTTP协议,可以期望不止一种类型的方法(例如GET,POST,PUT,DELETE,PATCH等)。 在大多数服务虚拟化方案中,我们有兴趣模拟特定于特定HTTP方法的行为。 这种情况是关于响应HTTP POST请求的,因此您需要将存根配置为仅与HTTP POST请求匹配:
{
"port"
:
3001 ,
"protocol"
:
"http" ,
"stubs"
:
[
{
"predicates"
:
[
{
"equals"
:
{
"method"
:
"post"
}
}
]
}
]
}
该冒名顶替者定义了一个仅与HTTP POST请求匹配(使用关键字equals )的谓词。
现在,仔细研究一下已实现的代码中定义的endPoint值:
string endPoint = "http://localhost:3001/api/v1/users/login" ;
除了侦听端口3001(在http:// localhost:3001中定义)外, endPoint更具体,因为它希望传入的HTTP POST请求进入/ api / v1 / users / login路径。 您如何告诉mountebank仅在/ api / v1 / users / login路径上完全匹配? 通过将路径键值对添加到存根的谓词中:
{
"port"
:
3001 ,
"protocol"
:
"http" ,
"stubs"
:
[
{
"predicates"
:
[
{
"equals"
:
{
"method"
:
"post" ,
"path"
:
"/api/v1/users/login"
}
}
]
}
]
}
现在,这个冒名顶替者知道到达端口3001的HTTP请求必须是POST方法,并且必须指向/ api / v1 / users / login路径。 剩下要做的唯一模拟是预期的HTTP响应。
将响应添加到JSON冒名顶替者:
{
"port"
:
3001 ,
"protocol"
:
"http" ,
"stubs"
:
[
{
"predicates"
:
[
{
"equals"
:
{
"method"
:
"post" ,
"path"
:
"/api/v1/users/login"
}
}
] ,
"responses"
:
[
{
"is"
:
{
"statusCode"
:
200 ,
"body"
:
"Successful login."
}
}
]
}
]
}
使用mountebank冒名顶替者,您可以将响应定义为JSON键值对的集合。 在大多数情况下,只需声明响应是statusCode和body即可 。 这种情况正在模拟状态代码为OK(200)且包含简单消息“ 成功登录” (如接受标准中所指定)的正文的“快乐路径”响应。
如何运行虚拟化服务?
好的,既然您已经虚拟化了用户身份验证服务(至少是它的“快乐之路”),如何运行它?
请记住,您已经启动了mountebank,它报告说它正在内存中作为http:// localhost域运行。 Mountebank正在监听2525端口并接受订单。
太好了,现在您必须告诉Mountebank您已经准备好冒名顶替者。 你是怎样做的? 将HTTP POST请求发送到http:// localhost:2525 / imposters 。 请求正文必须包含您在上面创建的JSON。 有几种技术可以发送该请求。 如果您精通curl ,则使用它发送HTTP POST请求将是抵御冒名顶替者的最简单,最快的方法。 但是许多人喜欢一种更加用户友好的方式来将HTTP POST发送到mountebank。
最简单的方法是使用Postman 。 如果您下载并安装Postman,则可以将其指向http:// localhost:2525 / imposters ,从下拉菜单中选择POST方法,然后将imposter JSON复制并粘贴到原始正文中。
单击发送时,冒名顶替者将被创建,并且您应获得状态201(已创建)。
您的虚拟化服务正在运行! 您可以通过导航到tests文件夹并运行dotnet test命令来验证它:
结论
该演示演示了通过模拟您依赖的服务来消除阻塞和控制依赖关系是多么容易。 Mountebank是一款出色的工具,可轻松,廉价地模拟各种非常精细,复杂的服务。
在这一期中,我只是有时间来说明如何虚拟化简单的“幸福之路”服务。 如果返回到实际的用户案例,您会注意到它的接受条件包含几个“不太满意”的路径(某些情况下,当有人反复尝试使用无效的凭据登录时)。 适当地虚拟化和测试这些用例会有些棘手,因此我将该练习留给了本系列的下一部分。
您将如何使用服务虚拟化解决您的测试需求? 我希望在评论中听到它。
翻译自: https://opensource.com/article/20/3/service-virtualization-test-driven-development
虚拟串口驱动 开发