翼度科技»论坛 编程开发 .net 查看内容

使用TestContainers在Docker中进行集成测试

5

主题

5

帖子

15

积分

新手上路

Rank: 1

积分
15
现代软件应用很少独立工作。典型的应用程序会与几个外部系统进行通信,如:

  • 数据库、
  • 消息系统、
  • 缓存提供商
  • 其他第三方服务。
你应该编写测试确保一切正常运行。
单元测试有助于隔离地测试业务逻辑,不涉及任何外部服务。它们易于编写并提供几乎即时的反馈。
有了单元测试还不够,集成测试用来验证与外部系统的交互情况,让你对你的应用程序完全有信心。
所以,在本周的时事通讯中,我将向你展示如何使用Docker进行集成测试。我们需要以下组件

  • TestContainers
  • Docker
  • xUnit
TestContainers是什么
Testcontainers 是一个用于使用临时 Docker 容器编写测试的库。
集成测试是“困难”的,因为你需要维护测试基础设施。在运行测试之前,你需要确保数据库已启动并正在运行。你还必须为测试提供所需的任何数据。如果你的测试在同一数据库上并行运行,它们可能会相互干扰。
一个可能的解决方案是使用所需服务的内存中变体。但这与使用mocks并没有太大的不同。内存中的服务可能没有生产服务的所有功能。
Testcontainers 通过使用 Docker 来启动真实服务来解决这个问题,以进行集成测试。
下面是创建 SQL Server 容器的示例:
  1. MsSqlContainer dbContainer = new MsSqlBuilder()
  2.     .WithImage("mcr.microsoft.com/mssql/server:2022-latest")
  3.     .WithPassword("Strong_password_123!")
  4.     .Build();
复制代码
之后,你可以使用 MsSqlContainer 实例来获取运行在容器内部的数据库的连接。 这是一个真实的SQL Server数据库,而不是内存数据库。
自定义 WebApplicationFactory:
ASP.NET Core 提供了一个内存测试服务器,我们可以用它来启动一个应用程序实例来运行测试。Microsoft.AspNetCore.Mvc.Testing 包提供了我们将用作实现基础的 WebApplicationFactory 类。
WebApplicationFactory 用于为集成测试创建一个 TestServer。
IntegrationTestWebAppFactory 进行以下工作:

  • 创建并配置MySqlContainer实例
  • 调用ConfigureTestServices 用容器中的数据库来设置EF Core
  • 用IAsyncLifetime来控制容器实例的启动/停止
  1. public class IntegrationTestWebAppFactory
  2.     : WebApplicationFactory<Program>,
  3.       IAsyncLifetime
  4. {
  5.     private readonly MsSqlContainer _dbContainer = new MsSqlBuilder()
  6.         .WithImage("mcr.microsoft.com/mssql/server:2022-latest")
  7.         .WithPassword("Strong_password_123!")
  8.         .Build();
  9.     protected override void ConfigureWebHost(IWebHostBuilder builder)
  10.     {
  11.         builder.ConfigureTestServices(services =>
  12.         {
  13.             var descriptorType =
  14.                 typeof(DbContextOptions<ApplicationDbContext>);
  15.             var descriptor = services
  16.                 .SingleOrDefault(s => s.ServiceType == descriptorType);
  17.             if (descriptor is not null)
  18.             {
  19.                 services.Remove(descriptor);
  20.             }
  21.             services.AddDbContext<ApplicationDbContext>(options =>
  22.                 options.UseSqlServer(_dbContainer.GetConnectionString()));
  23.         });
  24.     }
  25.     public Task InitializeAsync()
  26.     {
  27.         return _dbContainer.StartAsync();
  28.     }
  29.     public new Task DisposeAsync()
  30.     {
  31.         return _dbContainer.StopAsync();
  32.     }
  33. }
复制代码
MsSqlContainer 有一个 GetConnectionString 方法,用于获取当前容器的连接字符串。连接字符串可能会在测试之间发生变化,因为每个测试类都会创建一个单独的容器实例。在同一测试类中的测试案例将使用相同的容器实例。因此,如果你需要在测试之间进行清理,请记住这一点。
另一件事是Data Migration。你必须在每次测试之前显示运行,以创建所需的数据库结构。
使用 IAsyncLifetime 异步启动容器实例。在运行任何测试之前,容器是在 StartAsync 中启动的。而它是在 StopAsync 中停止的。
创建测试基类
测试基类将实现一个类固件接口 IClassFixture。并在测试用例之间提供共享的对象实例。这是实例化大多数测试所需的任何服务的地方。
例如,我正在创建一个 IServiceScope,用于在测试中解析scoped services。

  • ISender 用来发送command和queries
  • ApplicationDbContext 用来设置数据库或者验证结果
  1. public abstract class BaseIntegrationTest
  2.     : IClassFixture<IntegrationTestWebAppFactory>,
  3.       IDisposable
  4. {
  5.     private readonly IServiceScope _scope;
  6.     protected readonly ISender Sender;
  7.     protected readonly ApplicationDbContext DbContext;
  8.     protected BaseIntegrationTest(IntegrationTestWebAppFactory factory)
  9.     {
  10.         _scope = factory.Services.CreateScope();
  11.         Sender = _scope.ServiceProvider.GetRequiredService<ISender>();
  12.         DbContext = _scope.ServiceProvider
  13.             .GetRequiredService<ApplicationDbContext>();
  14.     }
  15.     public void Dispose()
  16.     {
  17.         _scope?.Dispose();
  18.         DbContext?.Dispose();
  19.     }
  20. }
复制代码
 
现在万事俱备,可以写测试用例了。
编写测试用例
这里有一个ProductTests类,里面有一个集成测试的用例,我们使用Arragne, Act, Assert结构。
  1. public class ProductTests : BaseIntegrationTest
  2. {
  3.     public ProductTests(IntegrationTestWebAppFactory factory)
  4.         : base(factory)
  5.     {
  6.     }
  7.     [Fact]
  8.     public async Task Create_ShouldCreateProduct()
  9.     {
  10.         // Arrange
  11.         var command = new CreateProduct.Command
  12.         {
  13.             Name = "AMD Ryzen 7 7700X",
  14.             Category = "CPU",
  15.             Price = 223.99m
  16.         };
  17.         // Act
  18.         var productId = await Sender.Send(command);
  19.         // Assert
  20.         var product = DbContext
  21.             .Products
  22.             .FirstOrDefault(p => p.Id == productId);
  23.         Assert.NotNull(product);
  24.     }
  25. }
复制代码
 
结束语
Testcontainers 是使用 Docker 编写集成测试的出色解决方案。你可以启动并配置任何 Docker 镜像,并从你的应用程序中使用它。这比使用模拟或内存变体要好得多,它们缺乏许多功能。
如果你有一个支持 Docker 的 CI/CD 流水线,Testcontainers 将开箱即用。
有几个集成测试可以大大增强你对系统的信心。
 
 
来源:https://www.cnblogs.com/odyssey/archive/2023/09/17/17709833.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具