在 .NET 8 中使用 WireMock.NET 进行集成测试

图片

概述:在这篇文章中,我们将介绍如何创建和管理模拟 API,以便使用 WireMock.NET 进行集成测试。集成测试保证了软件系统内不同组件之间的无缝通信,并且识别并解决了任何潜在问题。这种测试方法对于验证我们的应用程序与内部和外部 API 之间的交互至关重要,确保遵守预定义的合同和所需的功能。WireMock.NET 是一个强大的工具包,用于复制 HTTP API 行为,非常适合在各种场景中模拟 API。无论是测试依赖于 API 的类,还是使用外部 API 进行集成测试,WireMock.NET 都能提供全面的功能来有效模拟 API 行为。为什么我们需要模拟 API?我们需要模拟外部 API 的多

在这篇文章中,我们将介绍如何创建和管理模拟 API,以便使用 WireMock.NET 进行集成测试。集成测试保证了软件系统内不同组件之间的无缝通信,并且识别并解决了任何潜在问题。这种测试方法对于验证我们的应用程序与内部和外部 API 之间的交互至关重要,确保遵守预定义的合同和所需的功能。

WireMock.NET 是一个强大的工具包,用于复制 HTTP API 行为,非常适合在各种场景中模拟 API。无论是测试依赖于 API 的类,还是使用外部 API 进行集成测试,WireMock.NET 都能提供全面的功能来有效模拟 API 行为。

为什么我们需要模拟 API? 我们需要模拟外部 API 的多种方案,如下所述:

  • 外部 API 可能仍在开发中。尽管我们已经就详细说明 URL 结构和有效负载格式等内容的合同达成一致,但我们仍然需要满足最后期限。因此,即使在外部 API 完全运行之前,我们也需要确保我们的逻辑正常运行。

  • 有时,外部 API 可能缺少专用的测试环境。在这种情况下,模拟这些 API 并在本地运行测试以验证应用程序的行为是可行的。

  • 构建健壮的软件涉及处理各种异常情况。但是,并非所有 API 都提供全面的测试方案。模拟允许我们模拟不同的 API 行为,并测试代码对潜在错误的弹性。

  • 负载测试对于评估重负载下的软件性能至关重要。为了有效地进行负载测试,我们必须模拟外部依赖关系。模拟 API 使我们能够创建模拟环境,模仿真实外部服务的行为,从而促进全面的负载测试。

现在我们已经了解了为什么我们需要模拟 API 进行集成测试,让我们编写代码并了解如何使用 WireMock.NET。WireMock 是用于模拟 API 的流行工具,而 WireMock.NET 是为 .NET 量身定制的实现。

代码演练 让我们设想一个场景:我们正在为一家房地产公司开发软件。作为此项目的一部分,我们的任务是创建一个 API 来检索居民的所有未结余额。为了实现这一点,我们的 API 需要与公用事业公司 API 交互,以获取公用事业的欠款。我们将此交互封装在一个 UtilityService.cs 类中,负责对外部 Utility Company API 进行必要的调用并处理响应。

一旦我们实现了 UtilityService,我们的下一步就是通过集成测试来验证其功能。由于公用事业公司 API 可能不容易用于测试,因此我们将使用 WireMock.NET 来模拟其行为。这个工具允许我们创建模拟服务器来模拟实际 API 的响应,使我们能够在各种场景下彻底测试我们的 UtilityService。通过这些集成测试,我们将确保我们的 API 按预期运行,并准确地从公用事业公司 API 检索和处理居民余额信息。

using System.Net;
using System.Text.Json;

namespace RealEstate.Business.Utilities
{
    public class UtilityService
    {
        private readonly HttpClient _httpClient;
        public UtilityService(HttpClient httpClient)
        {
            _httpClient = httpClient ??
                throw new ArgumentNullException(nameof(httpClient));
        }
        public async Task<ResidentUtilityDto?> GetResidentUtilityBalanceByIdAsync(int customerId)
        {
            var response = await _httpClient.GetAsync($"/balances/v2/{customerId}");
            if (response.StatusCode == HttpStatusCode.OK)
            {
                try
                {
                    return JsonSerializer.Deserialize<ResidentUtilityDto>(await response.Content.ReadAsStringAsync());
                }
                catch
                {
                    return null;
                }
            }
            return null;
        }
    }
}

我们将看到对 WireMock 进行建模以模拟案例的不同方法。

案例

[Test]
public async Task GivenValidResident_WhenGetResidentUtilityBalanceByIdAsyncIsInvoked_ThenValidResidentUtilityBalanceIsReturned()
{
    //Arrange
    var customerId = 23;
    var faker = new ResidentUtilityDtoFaker(customerId);
    var residentBalance = faker.Generate();
    _mockServer.Given(Request.Create().UsingGet().WithPath($"/balances/v2/{customerId}"))
               .RespondWith(Response.Create().WithStatusCode(HttpStatusCode.OK).WithBodyAsJson(residentBalance));

    //Act
    var balanceResponse = await _utilityService.GetResidentUtilityBalanceByIdAsync(customerId);

    //Assert
    balanceResponse.Should().NotBeNull();
    balanceResponse.ElectricityBalance.Should().Be(residentBalance.ElectricityBalance);
    balanceResponse.TrashBalance.Should().Be(residentBalance.TrashBalance);
    balanceResponse.WaterBalance.Should().Be(residentBalance.WaterBalance);
}

案例

[Test]
public async Task GivenInValidResident_WhenGetResidentUtilityBalanceByIdAsyncIsInvoked_ThenNullIsReturned()
{
    //Arrange
    var customerId = 42;
    _mockServer.Given(Request.Create().UsingGet().WithPath($"/balances/v2/{customerId}"))
               .RespondWith(Response.Create().WithStatusCode(HttpStatusCode.NotFound));

    //Act
    var balanceResponse = await _utilityService.GetResidentUtilityBalanceByIdAsync(customerId);

    //Assert
    balanceResponse.Should().BeNull();
}

延迟响应

模拟延迟以响应模拟真实世界的场景。

[Test]
public async Task GivenValidResident_WhenGetResidentUtilityBalanceByIdAsyncIsInvoked_ShouldHandleDelayedResponses()
{
    //Arrange
    var customerId = 23;
    var faker = new ResidentUtilityDtoFaker(customerId);
    var residentBalance = faker.Generate();
    _mockServer.Given

(Request.Create().UsingGet().WithPath($"/balances/v2/{customerId}"))
               .RespondWith(Response.Create().WithStatusCode(HttpStatusCode.OK)
               .WithBodyAsJson(residentBalance).WithDelay(TimeSpan.FromMilliseconds(200)));

    //Act
    var watch = new Stopwatch();
    watch.Start();
    var balanceResponse = await _utilityService.GetResidentUtilityBalanceByIdAsync(customerId);
    watch.Stop();

    //Assert
    balanceResponse.Should().NotBeNull();
    watch.ElapsedMilliseconds.Should().BeGreaterThan(0);
}

模拟部分响应等故障,并了解我们如何处理代码内。

[Test]
public async Task GivenValidResident_WhenGetResidentUtilityBalanceByIdAsyncIsInvoked_ShouldHandleFaults()
{
    //Arrange
    var customerId = 23;
    var faker = new ResidentUtilityDtoFaker(customerId);
    var residentBalance = faker.Generate();
    _mockServer.Given(Request.Create().UsingGet().WithPath($"/balances/v2/{customerId}"))
               .RespondWith(Response.Create().WithFault(FaultType.MALFORMED_RESPONSE_CHUNK));

    //Act
    var balanceResponse = await _utilityService.GetResidentUtilityBalanceByIdAsync(customerId);

    //Assert
    balanceResponse.Should().BeNull();
}

还有更多用途,例如标头匹配、设置请求的优先级,甚至我们可以模拟 Webhook 等。WireMock.Net 是一个非常强大的工具,我们将在以后的博客中看到它如何帮助我们进行负载测试。

引入地址 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值