我已经有了基于
inMemoryDatabase & XUnit
的集成测试,所以我决定通过 testcontainers 3.5
为 docker 镜像重构它。我试图独自解决它,但陷入了死胡同。
问题是开始创建容器的核心,一开始你应该创建配置并下一步实现
IAsyncLifetime
。完成此步骤后,只需打开 docker,然后运行测试,这是基本的操作方法。经过这些步骤后,我遇到了两个错误:
"System.TimeoutException: The operation has timed out."
。我不知道为什么,但它出现在 XUnit
方法 "CreateClassFixtureAsync(Type fixtureType)"
中并掉落在这里:
"Docker API responded with status code=Conflict, response={message:Container %some_id% is not running}"
发生这种情况是因为在启动的同时,Docker 容器中的状态已更改为 Exited
。
我的
Factory.cs
创建容器的类:
public class IntegrationTestApplicationFactory : WebApplicationFactory<Program>, IAsyncLifetime
{
private const string ImageName = "ubuntu:16.04"; // tips #1
private const string Database = "master";
private const string Username = "sa";
private const string Password = "myStr0ng!&Passw0rd!";
private int MsSqlPort = Random.Shared.Next(10000, 60000);
private readonly IContainer _mssqlContainer;
public IntegrationTestMalumApplicationFactory()
{
_mssqlContainer = new ContainerBuilder()
.WithImage(ImageName)
.WithName(Guid.NewGuid().ToString())
.WithEnvironment("ACCEPT_EULA", "Y")
.WithEnvironment("SQLCMDUSER", Username)
.WithEnvironment("SQLCMDPASSWORD", Password)
.WithEnvironment("MSSQL_SA_PASSWORD", Password)
.WithEnvironment("MSSQL_PID", "Evaluation")
.WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(MsSqlPort))
.WithPortBinding(MsSqlPort, true)
.WithExposedPort(MsSqlPort)
.WithCleanUp(true)
.Build();
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
var host = _mssqlContainer.Hostname;
var port = _mssqlContainer.GetMappedPublicPort(MsSqlPort);
builder.ConfigureTestServices(services =>
{
services.RemoveAll(typeof(DbContextOptions<DataAccess.Context>));
services.AddDbContextPool<DataAccess.Context>((options, optionsBuilder) =>
{
optionsBuilder.UseSqlServer(
$"Server={host},{port};Database={Database};User Id={Username};Password={Password};TrustServerCertificate=true");
});
});
}
public async Task InitializeAsync() => await _mssqlContainer.StartAsync();
public async new Task DisposeAsync() => await _mssqlContainer.StopAsync();
}
我的
Test.cs
看起来像:
public class IntegrationTestMalumApi : IClassFixture<IntegrationTestApplicationFactory>
{
protected readonly HttpClient TestClient;
protected readonly ApplicationUrl ApplicationUrl;
public IntegrationTestMalumApi(IntegrationTestApplicationFactory applicationFactory)
{
TestClient = applicationFactory.CreateClient();
ApplicationUrl = new ApplicationUrl("http://localhost:1324");
}
[Fact]
public async Task GetCats_WithThreeCats_ReturnHttpCodeOK()
{
// to do something
}
}
一些提示:
尝试使用
"mcr.microsoft.com/mssql/server:2022-preview-ubuntu-22.04"
,但它实际上仅适用于现有的imageName
;
ApplicationUrl
是包含任何 API 路径的路由的类;
问题发生在班级
"CreateClassFixtureAsync(Type fixtureType)"
"XunitTestClassRunner.cs"
;
有时,每当尝试使用
"InitializeAsync()"
时,都会在班级
"IntegrationTestApplicationFactory "
中发生这种情况
"_mssqlContainer.StartAsync()";
这样使用基本配置的东西时,它也不起作用。
你有什么建议吗?我做错了什么?
@AlwaysLearning回答后,我重新设计了它。
解决方案:所以,他怎么说主要原因是在港口。默认情况下,
MSSQL的端口应等于"IContainer container = new ContainerBuilder().WithImage(ImageName).Build()"
,这是我问题的答案,但不是结束。接下来我收到了基于配置的异常,修复是下一个字符串:
"1433"
。接下来看代码:
"ACCEPT_EULA=Y"
更多信息,我重新设计了
private const string ImageName = "mcr.microsoft.com/mssql/server:2022-preview-ubuntu-22.04";
private const ushort MssqlPort = 1433;
private const string Database = "master";
private const string Username = "sa";
private const string Password = "myStr0ng!&Passw0rd!";
private const string ContainerName = "integrationtest";
private readonly IContainer _mssqlContainer;
public IntegrationTestApplicationFactory()
{
_mssqlContainer = new ContainerBuilder()
.WithImage(ImageName)
.WithName(String.Concat(ContainerName, Guid.NewGuid().ToString()))
.WithEnvironment("ACCEPT_EULA", "Y")
.WithEnvironment("SQLCMDUSER", Username)
.WithEnvironment("SQLCMDPASSWORD", Password)
.WithEnvironment("MSSQL_SA_PASSWORD", Password)
.WithEnvironment("MSSQL_PID", "Evaluation")
.WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(MssqlPort))
.WithPortBinding(MssqlPort, true)
.WithCleanUp(true)
.Build();
}
创建 EF 迁移的方法,来源
这里:
InitializeAsync
有用的链接: