前一篇文章「建立測試用 Container 前先建立 Docker Image - 使用 FluentDocker」是在 xUint 測試專案裡使用了 FluentDocker 和 Testcontainers 分別建立 Docker Image 與 Docker Container,這一篇就單純地展示完全使用 FluentDocker 來完成 Docker Image 和 Docker Container 的建立。
測試專案使用 FluentDocker
因為是 xUnit 測試專案,雖然也沒有用到套件裡所提供的類別、功能,但也還是安裝 Ductus.FluentDocker.XUint
至於 Ductus.FluentDocker.XUint 有提供了什麼給 xUnit 的功能,可以看以下的說明文件內容
修改 MongoFixture 檔案
原本的 MongoFixture.cs 檔案內容可以詳閱前一篇文章的內容「建立測試用 Container 前先建立 Docker Image - 使用 FluentDocker」
原本裡面建立 Docker Image 的部分還是保持原來使用 docker-compose.yml 建立的方式,之後再來說明如果要全部改用 docker-compose.yml 建立 image 和 container 應該要怎麼做
在 MongoFixture.cs 裡新增 CreateMongoDockerContainer 方法
private IContainerService _containerService;
/// <summary>
/// 建立測試用的 MongoDB Docker-container.
/// </summary>
private void CreateMongoDockerContainer()
{
var databaseSettings = TestSettingProvider.GetDatabaseSettings();
DatabaseIp = "127.0.0.1";
DatabasePort = databaseSettings.HostPort;
var containerPort = databaseSettings.ContainerPort;
var environmentName = TestSettingProvider.GetEnvironmentName();
var environmentSettings = databaseSettings.EnvironmentSettings.Select(environmentSetting => $"{environmentSetting.Key}={environmentSetting.Value}").ToArray();
// Create MongoDB Container
this._containerService = new Builder().UseContainer()
.DeleteIfExists(force: true)
.UseImage($"{databaseSettings.Image}")
.WithName($"{environmentName}")
.ExposePort(hostPort: DatabasePort, containerPort: containerPort)
.WaitForPort($"{containerPort}/tcp", TimeSpan.FromSeconds(10))
.WithEnvironment(environmentSettings)
.Build()
.Start();
}
將 MongoFixture.cs 原本繼承 IAsyncLifetime 介面拿掉,也需要移除 InitializeAsync 和 DisposeAsync 這兩個方法,然後改為繼承實做 IDisposable,測試執行完畢後要移除 docker container 則是要在 Dispose 方法裡處理,建立 Docker Image 與 Docker Container 會是在 建構式裡執行,調整後的程式內容如下:
private IContainerService _containerService;
public MongoFixture()
{
CreateMongoDockerImage();
this.CreateMongoDockerContainer();
// FluentAssertions - Setup DateTime AssertionOptions
SetupDateTimeAssertions();
// MongoDb Class Mapping
Mapping.RegisterClassMapping();
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing is false)
{
return;
}
this._containerService.Dispose();
this._containerService= null;
}
因為 MongoFixture 這個類別是給 MongoCollectonFixture 這個繼承 ICollecitonFixture<T> 所使用的,所以當整個使用 MongoCollectonFixture 都完成測試後,最後就會去執行 MongoFixture 類別解構式裡的內容,
[CollectionDefinition(nameof(MongoCollectionFixture))]
public class MongoCollectionFixture : ICollectionFixture<MongoFixture>
{
}
每個有需要使用到 MongoDB 的 RepositoryTests 測試類別,就要在類別上去標示使用 CollectionAttribute 並設定 MongoCollectionFixture
如果你對 xUnit 的 Shared Context 以及不同測試類別的建構式、解構式的作用還不是很瞭解的話,就請直接查看官方文件
完整的 MongoFixture 類別內容
以下是完整 MongoFixture.cs 程式內容
public class MongoFixture : IDisposable
{
private IContainerService _containerService;
public MongoFixture()
{
CreateMongoDockerImage();
this.CreateMongoDockerContainer();
// FluentAssertions - Setup DateTime AssertionOptions
SetupDateTimeAssertions();
// MongoDb Class Mapping
Mapping.RegisterClassMapping();
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing is false)
{
return;
}
this._containerService.Dispose();
this._containerService= null;
}
/// <summary>
/// 建立測試用的 MongoDB Docker-image.
/// </summary>
private static void CreateMongoDockerImage()
{
// 使用 FluentDocker (https://github.com/mariotoffia/FluentDocker)
// 透過 FluentDocker 的 UseCompose 功能執行 docker-compose.yml
// 執行 docker-compose 就會建立 MongoDB docker-image
var dockerComposeFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Docker", "Mongo", "docker-compose.yml");
using var svc = new Builder().UseContainer().UseCompose().FromFile(dockerComposeFile).RemoveOrphans().Build().Start();
}
/// <summary>
/// 建立測試用的 MongoDB Docker-container.
/// </summary>
private void CreateMongoDockerContainer()
{
var databaseSettings = TestSettingProvider.GetDatabaseSettings();
DatabaseIp = "127.0.0.1";
DatabasePort = databaseSettings.HostPort;
var containerPort = databaseSettings.ContainerPort;
var environmentName = TestSettingProvider.GetEnvironmentName();
var environmentSettings = databaseSettings.EnvironmentSettings.Select(environmentSetting => $"{environmentSetting.Key}={environmentSetting.Value}").ToArray();
// Create MongoDB Container
this._containerService = new Builder().UseContainer()
.DeleteIfExists(force: true)
.UseImage($"{databaseSettings.Image}")
.WithName($"{environmentName}")
.ExposePort(hostPort: DatabasePort, containerPort: containerPort)
.WaitForPort($"{containerPort}/tcp", TimeSpan.FromSeconds(10))
.WithEnvironment(environmentSettings)
.Build()
.Start();
}
/// <summary>
/// FluentAssertions - Setup DateTime AssertionOptions
/// </summary>
private static void SetupDateTimeAssertions()
{
// FluentAssertions 設定 : 日期時間使用接近比對的方式,而非完全一致的比對
AssertionOptions.AssertEquivalencyUsing(options =>
{
options.Using<DateTime>(ctx => ctx.Subject.Should().BeCloseTo(ctx.Expectation, TimeSpan.FromMilliseconds(1000)))
.WhenTypeIs<DateTime>();
options.Using<DateTimeOffset>(ctx => ctx.Subject.Should().BeCloseTo(ctx.Expectation, TimeSpan.FromMilliseconds(1000)))
.WhenTypeIs<DateTimeOffset>();
return options;
});
}
}
可以看到這邊修改後的內容就已經沒有使用到 Testcontainers 了,所以你想要單純只使用一種 Docker for Test 的套件就可以參考這一篇的方式來試試看。
以上
純粹是在寫興趣的,用寫程式、寫文章來抒解工作壓力