C#中实现契约测试的方法

 更新时间:2023年09月15日 09:02:19   作者:杨中科  
这篇文章主要介绍了C#中实现契约测试,在本文中,我将揭开契约测试的神秘面纱,并向您展示如何在 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”部分。

到此这篇关于C#中实现契约测试的方法的文章就介绍到这了,更多相关C#契约测试内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • WPF实现绘制折线图的示例代码

    WPF实现绘制折线图的示例代码

    这篇文章主要为大家详细介绍了如何使用WPF实现绘制简单的折线图,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-04-04
  • C#中File和FileStream的简单介绍和用法

    C#中File和FileStream的简单介绍和用法

    这篇文章主要给大家介绍了关于C#中File和FileStream用法的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • c# RPC框架的使用简介

    c# RPC框架的使用简介

    这篇文章主要介绍了c# RPC框架的使用简介,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下
    2021-02-02
  • C#实现简单的天气预报示例代码

    C#实现简单的天气预报示例代码

    这篇文章主要介绍了C#实现简单的天气预报示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • winform 使用Anchor属性进行界面布局的方法详解

    winform 使用Anchor属性进行界面布局的方法详解

    这篇文章主要介绍了winform 使用Anchor属性进行界面布局的方法,有需要的朋友可以参考一下
    2013-12-12
  • C#实现UDP通信方式

    C#实现UDP通信方式

    文章介绍了如何使用C#实现UDP通信,包括UDP服务器和客户端的实现步骤和示例代码,服务器关键类为UdpClient和IPEndPoint,实例化对象后可以通过异步任务发送数据并接收数据,客户端同样使用UdpClient和IPEndPoint,连接到远程服务器后开新任务接收数据
    2025-01-01
  • C# StringBuilder和string

    C# StringBuilder和string

    这篇文章主要介绍了C# StringBuilder和string,文章围绕StringBuilder和string的相关资料展开内容,需要的朋友可以参考一下
    2021-11-11
  • 不能在子类或外部类发布C#事件代码分析

    不能在子类或外部类发布C#事件代码分析

    一个朋友问了一个问题:“为什么不能在子类或外部发布C#事件?”,我说我不知道,要看看生产的IL代码,下面我们看看
    2013-11-11
  • Revit API取得变量的内参名称实例代码

    Revit API取得变量的内参名称实例代码

    这篇文章介绍了Revit API取得变量的内参名称实例代码,有需要的朋友可以参考一下
    2013-11-11
  • oledb连接access数据库示例

    oledb连接access数据库示例

    本文为大家介绍一个oledb连接access数据库的示例,代码很简单,可以直接使用
    2014-01-01

最新评论