C# 与 Docker 集成测试:完整指南

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

使用Docker进行C#集成测试:如何摆脱环境问题



每位C#开发者都遇到过这种情况:测试在本地机器上通过,但在CI服务器上却失败了。原因往往是环境差异:数据库版本不同、Redis设置不一致、缺少测试服务。使用Docker进行C#集成测试可以一劳永逸地解决这个问题。在本文中,我们将探讨如何利用容器搭建隔离的测试环境,编写真正的集成测试,并将其集成到CI/CD流水线中。



为什么Docker是C#集成测试的理想工具



集成测试旨在检查系统组件之间的交互:应用程序+数据库、API+缓存、微服务之间的协作。如果没有Docker,你将不得不:

  • 在每位开发者的机器上安装PostgreSQL、SQL Server、Redis
  • 在CI服务器上配置完全相同的版本
  • 在每次测试运行之间清理数据

Docker可以解决以上所有问题。你可以用代码(Dockerfile、docker-compose.yml)描述基础设施,它会在任何系统上以相同方式部署。对于C#(.NET)来说,这尤其重要,因为微软积极支持容器化:提供官方.NET SDK镜像、Linux容器中的SQL Server、以及与Azure DevOps的集成。



项目搭建:Testcontainers + xUnit



为了使用Docker进行C#集成测试,我们将采用Testcontainers库(可在NuGet上获取)。它能自动管理容器的生命周期:在测试前启动,在测试完成后停止。



第一步:安装NuGet包



创建一个新的测试项目,或打开现有项目。添加NuGet包:

dotnet add package Testcontainersdotnet add package Testcontainers.PostgreSqldotnet add package xunitdotnet add package FluentAssertions


第二步:基于容器的测试基类



我们来创建一个抽象类,它会在运行测试之前启动PostgreSQL:

using Testcontainers.PostgreSql;using Xunit;

public abstract class IntegrationTestBase : IAsyncLifetime{ private readonly PostgreSqlContainer _postgresContainer;

protected string ConnectionString => _postgresContainer.GetConnectionString();

protected IntegrationTestBase() { _postgresContainer = new PostgreSqlBuilder() .WithImage("postgres:15-alpine") .WithDatabase("testdb") .WithUsername("test") .WithPassword("test123") .Build(); }

public async Task InitializeAsync() { await _postgresContainer.StartAsync(); }

public async Task DisposeAsync() { await _postgresContainer.StopAsync(); }}

该类实现了xUnit中的IAsyncLifetime接口——容器将在第一个测试前启动,并在最后一个测试后停止。GetConnectionString()方法会返回Docker分配给容器的动态端口。



第三步:编写集成测试



现在我们来编写一个测试,检验仓储层与真实数据库的交互:

public class UserRepositoryTests : IntegrationTestBase{    [Fact]    public async Task CreateUser_Should_SaveToDatabase()    {        // Arrange        await using var connection = new NpgsqlConnection(ConnectionString);        await connection.OpenAsync();                var repository = new UserRepository(connection);        var user = new User { Name = "Ivan", Email = "ivan@example.com" };

// Act var userId = await repository.CreateAsync(user);

// Assert userId.Should().BeGreaterThan(0); var savedUser = await repository.GetByIdAsync(userId); savedUser.Should().NotBeNull(); savedUser.Name.Should().Be("Ivan"); savedUser.Email.Should().Be("ivan@example.com"); }}

博客

书籍推荐