10分钟理解契约测试及如何在C#中实现
在软件开发中,确保微服务和API的可靠性和稳定性非常重要。 随着应用程序变得越来越复杂,对强大的测试策略的需求也越来越大,这些策略可以帮助团队在不牺牲敏捷性的情况下交付高质量的代码。 近年来获得广泛关注的一种方法是契约测试(Contract Testing)。 在本文中,我将揭开契约测试的神秘面纱,并向您展示如何在 C# 项目中实现它。
1. 术语
消费者(Consumer):对服务进行消费的代码,通常指的是客户端。
提供者(Provider): 提供服务的代码,通常指的是服务器端。
契约(Contract): 消费者和提供者之间商定的协议。 它包括预期的请求(输入)和响应(输出)。
2. 为什么需要契约测试?
构建和维护微服务是一项艰巨的任务。 在众多服务必须彼此无缝交互的世界中,确保对一项服务的更改不会破坏另一项服务的功能是很让人头疼的。 传统的集成测试针对的是整个系统之间的交互,工作量太大、速度太慢,甚至无法直接识别问题。 与之相反的是,契约测试侧重于测试各个服务之间的契约。 合同测试根据消费者和提供商之间商定的契约分别对消费者和提供商进行测试。
3. 如何执行契约测试
在契约测试中,消费者端程序员编写“消费者测试”,其中包含期望的输入和输出,并且期望将被保存到 Pact Json 文件中。 运行时,测试将请求发送到内置的模拟服务器而不是真实服务器,模拟服务器使用保存的 Pact Json 文件发送响应,该响应将用于验证消费者端测试用例。
此外,契约测试框架将读取保存的 Pact Json 文件,并向服务提供者(服务器)发送请求,并且将根据 Pact Json 文件中的预期输出来验证响应。
4. What is Pact?
Pact 是合约测试的实现。 由于消费者和提供者可能使用不同的编程语言进行开发,因此 Pact 是语言无关的,它支持多种编程语言,例如 Java、.NET、Ruby、JavaScript、Python、PHP 等。保存的 Pact Json 文件是由 用一种编程语言开发的消费者可以用来验证用另一种编程语言开发的提供者。
在本文中,消费者和提供者都是使用.NET (C#) 开发的。 Pact.Net 是 Pact 在 .Net 中的实现。
5. 如何使用Pact.Net?
使用Pact.Net总共分三步:开发一个待测试的WebAPI服务;编写消费者端测试用例;编写提供者端测试用例。
a. 开发待测试的WebAPI服务
创建一个 ASP.Net Core WebAPI项目,然后如下编写一个简单的控制器。
[ApiController]
[Route("[controller]/[action]")]
public class MyController : ControllerBase
{
[HttpGet]
public int Abs(int i)
{
return Math.Abs(i);
}
}
上面的控制器提供了一个计算给定整数的绝对值的简单服务。
Pact需要使用ASP.Net Core项目的Startup类来启动Web服务器,但是,在最新的.NET Core中,传统的Startup.cs被Minimal API取代。如果 要将 Pact 与 .NET Core 一起使用,您必须切换到 传统Startup 风格的代码,如果您不知道如何切换回传统的Startup 风格的代码,请 搜索“Adding Back the Startup Class to ASP.NET Core”。
b. 编写消费者端测试用例
创建一个使用xUnit的.NET测试项目,然后在测试项目上安装“PactNet”这个Nuget包。然后编写如下的测试用例。
public class UnitTest1
{
private readonly IPactBuilderV4 pactBuilder;
public UnitTest1()
{
var pact = Pact.V4("MyAPI consumer", "MyAPI",new PactConfig());
this.pactBuilder = pact.WithHttpInteractions();
}
[Fact]
public async Task Test1()
{
this.pactBuilder.UponReceiving("A request to calc Abs")
.Given("Abs")
.WithRequest(HttpMethod.Get, "/My/Abs")
.WithQuery("i","-2")//Match.Integer(-2)
.WillRespond()
.WithStatus(HttpStatusCode.OK)
.WithJsonBody(2); await this.pactBuilder.VerifyAsync(async ctx=>
{
using HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = ctx.MockServerUri;
var r = await httpClient.GetFromJsonAsync<int>($"/My/Abs?i=-2");
Assert.Equal(2,r);
});
}
}
“WithRequest().WithQuery()”用于定义输入,“WillRespond().WithJsonBody()”用于定义相应的预期输出。VerifyAsync中的代码片段是测试用例,根据“UponReceiving”定义的期望进行测试。 从“httpClient.BaseAddress = ctx.MockServerUri”可以看出,Provider 测试用例与 Pact 提供的Mock服务器交互而不是真实服务器进行交互。
接下来,让我们运行测试,测试运行完成后,测试项目的pact文件夹下会生成一个“MyAPI Consumer-MyAPI.json”,这个Json文件中保存了预期的输入和输出,如下图。

c. 编写提供者端测试用例
创建一个使用xUnit的.NET测试项目,然后向其安装 Nuget 包“PactNet”和“PactNet.Output.Xunit”。 由于提供程序测试必须使用 Startup 类启动测试服务器,因此请将待测试的 ASP.NET Core WebAPI 项目的引用添加到提供程序测试项目中。
创建一个“MyApiFixture”类,用于启动测试项目中测试的WebAPI服务器。MyApiFixture类的代码如下:
public class MyApiFixture: IDisposable
{
private readonly IHost server;
public Uri ServerUri { get; }
public MyApiFixture()
{
ServerUri = new Uri("http://localhost:9223");
server = Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseUrls(ServerUri.ToString());
webBuilder.UseStartup<Startup>();
})
.Build();
server.Start();
} public void Dispose()
{
server.Dispose();
}
}
接下来,如下创建一个使用保存的Pact Json文件对服务器(提供者)进行测试的测试用例。
public class MyApiTest1: IClassFixture<MyApiFixture>
{
private readonly MyApiFixture fixture;
private readonly ITestOutputHelper output;
public MyApiTest1(MyApiFixture fixture,ITestOutputHelper output)
{
this.fixture = fixture;
this.output = output;
}
[Fact]
public async Task Test1()
{
var config = new PactVerifierConfig
{
Outputters = new List<IOutput>
{
new XunitOutput(output),
},
};
string pactPath = Path.Combine("..","..","..","..",
"TestConsumerProject1", "pacts", "MyAPI consumer-MyAPI.json");
using var pactVerifier = new PactVerifier("MyAPI", config);
pactVerifier
.WithHttpEndpoint(fixture.ServerUri)
.WithFileSource(new FileInfo(pactPath))
.Verify();
}
}
“pactPath”是指保存的Pact文件,在您的项目中,它会根据项目名称、相对路径的不同而不同。 执行上述测试时,Pact 将启动测试项目中的测试服务器,发送请求并根据保存的 Json 文件验证响应。
6. 对基于消息的服务使用Pact
Pact也支持对于基于消息的服务(也被称为async API)进行测试。详细请查看Pact文档的“messaging pacts”部分。
10分钟理解契约测试及如何在C#中实现的更多相关文章
- 10分钟理解BFC原理
10 分钟理解 BFC 原理 一.常见定位方案 在讲 BFC 之前,我们先来了解一下常见的定位方案,定位方案是控制元素的布局,有三种常见方案: 普通流 (normal flow) 在普通流中,元素按照 ...
- [转帖]SQL Server 10分钟理解游标
SQL Server 10分钟理解游标 https://www.cnblogs.com/VicLiu/p/11671776.html 概述 游标是邪恶的! 在关系数据库中,我们对于查询的思考是面向集合 ...
- T-SQL查询进阶-10分钟理解游标
转:http://www.cnblogs.com/CareySon/archive/2011/11/01/2231381.html 概述 游标是邪恶的! 在关系数据库中,我们对于查询的思考是面向集合的 ...
- SQL Server 10分钟理解游标
概述 游标是邪恶的! 在关系数据库中,我们对于查询的思考是面向集合的.而游标打破了这一规则,游标使得我们思考方式变为逐行进行.对于类C的开发人员来着,这样的思考方式会更加舒服. 正常面向集合的思维方式 ...
- 10分钟学会 API 测试 !
本文面向对象主要是后端开发人员 API 开发好之后,我们需要对 API 进行简单的调试,确保 API 可以跑通再提交给前端人员进行对接或者是测试人员对 API 进行测试: 在测试过程中我们关注 ...
- 10分钟理解JS引擎的执行机制
首先,请牢记2点: (1) JS是单线程语言 (2) JS的Event Loop是JS的执行机制.深入了解JS的执行,就等于深入了解JS里的event loop 1.灵魂三问 (1) JS为什么是单线 ...
- 10 分钟理解 BFC 原理
一.常见定位方案 在讲 BFC 之前,我们先来了解一下常见的定位方案,定位方案是控制元素的布局,有三种常见方案: 普通流 (normal flow) 在普通流中,元素按照其在 HTML 中的先后位置至 ...
- 10分钟理解Android数据库的创建与使用(附具体解释和演示样例代码)
1.Android数据库简单介绍. Android系统的framework层集成了Sqlite3数据库.我们知道Sqlite3是一种轻量级的高效存储的数据库. Sqlite数据库具有以下长处: (1) ...
- 10分钟理解React生命周期
前言 学习React,生命周期很重要,我们了解完生命周期的各个组件,对写高性能组件会有很大的帮助. 一.简介 React /riˈækt/ 组件的生命周期指的是组件从创建到销毁过程中所经历的一系列方法 ...
- 基于Pact的契约测试
背景 如今,契约测试已经逐渐成为测试圈中一个炙手可热的话题,特别是在微服务大行其道的行业背景下,越来越多的团队开始关注服务之间的契约及其契约测试. 什么是契约测试 关于什么是契约测试这个问题, ...
随机推荐
- Java(数组声明创建、初始化、特点、内存分析、边界)
数组的定义 数组是相同类型数据的有序集合 数组描述的是相同类型的若干数据,按照一定先后次序排序组合而成 其中,每一个数据称作一个数组元素,每个数组元素可以通过下标访问它们 1.数组声明和创建 声明 d ...
- 聊聊Spring Cloud Gateway
网关概述 整体来看,网关有点类似于门面,所有的外部请求都会先经过网关这一层. 网关不仅只是做一个请求的转发及服务的整合,有了网关这个统一的入口之后,它还能提供以下功能. 针对所有请求进行统一鉴权.限流 ...
- [Java SE] 彻底搞懂Java程序的三大参数配置途径:系统变量与JVM参数(VM Option)/环境变量/启动程序参数args
0 序言 一次没搞懂,处处受影响.这个问题属于基础问题,但又经常踩坑,不得不重视一下了. 1 Java程序动态参数的配置途径:系统变量与JVM参数(VM Option) vs 环境变量 vs 启动程序 ...
- ORM总览
ORM(Object-Relational Mapping)是一种常见的数据访问技术,它将对象模型和关系模型之间进行映射.ORM的主要作用是简化数据访问和管理,提高开发效率和代码质量.在实际应用中,O ...
- JVM源码分析:深入剖析java.c文件中JavaMain方法中InitializeJVM的实现
经过前文<从JDK源码级深入剖析main方法的运行机制>的分析,我们知道了实现JavaMain方法的四个主要步骤: 初始化Java虚拟机 加载主运行类 通过加载的主运行类,获取main方法 ...
- OAuth2.0andmultifactorauthentication:Howtocreateasecure
目录 1. 引言 2. 技术原理及概念 2.1. 基本概念解释 2.2. 技术原理介绍 2.3. 相关技术比较 3. 实现步骤与流程 3.1. 准备工作:环境配置与依赖安装 随着数字化时代的到来,人们 ...
- 记一次 .NET 某企业采购平台 崩溃分析
一:背景 1. 讲故事 前段时间有个朋友找到我,说他们的程序有偶发崩溃的情况,让我帮忙看下怎么回事,针对这种 crash 的程序,用 AEDebug 的方式抓取一个便知,有了 dump 之后接下来就可 ...
- 【阅读笔记】低照度图像增强-《Fast efficient algorithm for enhancement of low lighting video》
本文介绍的是一种比较实用的低照度图像增强效果很好的方法,Xuan Dong论文<Fast efficient algorithm for enhancement of low lighting ...
- matlab 整数提升为正整数幂
matlab 整数提升为正整数幂 在使用matlab 的gui界面绘制时报的错误, 是因为之前数据有非double类型的数据,但是有的数据看起来确实是double类型的,但是程序还是报错跑不下去 解决 ...
- 现代C++(Modern C++)基本用法实践:四、模板
概述 C++的模板是泛型编程思想的一种实现.C++是强类型语言,处处强调类型.同样的加法运算,int和float的加法运算需定义两个函数(重载),而使用模板则可以只用一个函数(见下面示例). 这类似我 ...