CT1*_*.IT 2 sql-server integration-testing docker .net-5
我有一个 .NET 5 解决方案,其中包含一个服务项目,其中包含一系列负责从数据库保存/加载数据的存储库(主要通过 EFCore)。
我正在为这个项目编写一些集成测试,因此想针对真实的 SQL Server 数据库测试操作。
使用 docker 是否可以让运行测试的过程启动和拆卸 SQL Server docker 容器,如果可以的话,我们该如何进行呢?
目前,我只是手动运行 docker-compose up 来启动容器,然后运行 dotnet test ,它有一个指向 docker 实例的连接字符串,并生成准备用于测试的虚拟数据。
我已经考虑过为测试项目创建一个 Dockerfile,但是根据我的理解,dockerfile 用于生成项目的映像,而且我实际上不想生成 docker 映像,我只想针对 docker 容器运行测试纯粹是为了测试范围而存在。
我已经成功地使用一个名为 dotnet testcontainers 的优秀 nuget 包来实现我所需要的内容(https://github.com/HofmeisterAn/dotnet-testcontainers)
这允许我从 C# 代码中启动一个标准的 sql server docker 映像,导入我的 sql 脚本,然后将其全部拆除。
我使用 xUnit 进行测试,因此我使用 CollectionDefinition 原则来启动容器一次,然后将其丢弃。
数据库夹具.cs
public class DatabaseFixture : IDisposable
{
private const int _hostPort = 1434;
private const int _containerPort = 1433;
private const string _databasePassword = "localdevpassword#123";
private const string _databaseUser = "sa";
private string _myDbContext_ConnectionString =>
$"Server=tcp:localhost,{_hostPort};Database=MyDbContext;user id={_databaseUser};password={_databasePassword};";
public TestcontainersContainer Container { get; private set; }
public MyDbContext MyDb { get; private set; }
public DatabaseFixture()
{
InitContainerTest();
var myDbContextContextOptions = new DbContextOptionsBuilder<MyDbContext>()
.UseSqlServer(_myDbContext_ConnectionString)
.Options;
MyDb = new MyDbContext(myDbContextContextOptions );
// Seed the database if required
}
public void Dispose()
{
Container.StopAsync();
Container.CleanUpAsync();
}
private void InitContainerTest()
{
var outputConsumer = Consume.RedirectStdoutAndStderrToStream(new MemoryStream(), new MemoryStream());
var waitStrategy = Wait.ForUnixContainer().UntilMessageIsLogged(outputConsumer.Stdout, "INIT_COMPLETE");
var containerBuilder = new TestcontainersBuilder<TestcontainersContainer>()
.WithImage("mcr.microsoft.com/mssql/server:2019-latest")
.WithPortBinding(_hostPort, _containerPort)
.WithEnvironment("ACCEPT_EULA", "true")
.WithEnvironment("SA_PASSWORD", _databasePassword)
.WithBindMount(Path.GetFullPath("sql"), "/scripts/")
.WithCommand("/bin/bash", "-c", "/scripts/init.sh")
.WithOutputConsumer(outputConsumer)
.WithWaitStrategy(waitStrategy);
Container = containerBuilder.Build();
var task = Container.StartAsync();
task.WaitAndUnwrapException();
}
}
Run Code Online (Sandbox Code Playgroud)
数据库集合.cs
[CollectionDefinition("Database collection")]
public class DatabaseCollection : ICollectionFixture<DatabaseFixture>
{
// This class has no code, and is never created. Its purpose is simply
// to be the place to apply [CollectionDefinition] and all the
// ICollectionFixture<> interfaces.
}
Run Code Online (Sandbox Code Playgroud)
用户服务测试.cs
[Collection("Database collection")]
public class UserServiceTests
{
private readonly UserService _sut;
private readonly DatabaseFixture _databaseFixture;
public UserServiceTests(DatabaseFixture databaseFixture)
{
_databaseFixture = databaseFixture;
_sut = new UserService(_databaseFixture.MyDb);
}
[Fact]
public async Task GetUserCompany_ShouldReturnCompanyId_WhenUserIdFound()
{
// Arrange
var faker = new Faker<Thing>();
faker.RuleFor(e => e.UserId, f => Guid.NewGuid());
faker.RuleFor(e => e.CompanyId, f => Guid.NewGuid());
var thing = faker.Generate();
_databaseFixture.MyDb.Things.Add(thing);
await _databaseFixture.MyDb.SaveChangesAsync();
// Act
var actualOutput = await _sut.GetCompanyIdFromUserId(thing.UserId);
// Assert
actualOutput.Should().Be(thing.CompanyId);
}
}
Run Code Online (Sandbox Code Playgroud)
需要做一些工作的事情
我需要在 Container.StartAsync() 之后手动添加延迟以允许数据库脚本运行。
我现在等待容器标准输出输出“INIT_COMPLETE”,这是复制的 init.sh 文件的最后一行。我现在还使用一点小技巧从 DatabaseFixture 中运行 StartAsync() 方法,以允许从非异步方法中运行异步代码。
var task = Container.StartAsync();
task.WaitAndUnwrapException();
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2134 次 |
| 最近记录: |