前一篇「xUnit 完全使用 FluentDocker 建立 MongoDB 的 Docker Image 和 Docker Container」裡用了兩個步驟分別建立 docker-image 和 docker-container,而建立 docker-image 是透過執行 docker-compose.yml 的方式。
如果不想要這麼麻煩地分成兩步驟,而是想要執行一次 docker-compose 就完成 docker-image 和 docker-container 的建立,這篇文章就來簡單說明如何進行。
之前有說過為什麼只有透過 FluentDocker 執行 docker-compose 建立 docker-image 的理由,因為我希望可以在建立 docker-container 時可以動態指定 port,如果是完全都使用 docker-compose.yml 的設定,那麼很多設定內容都會固定寫死的,就無法滿足我想要動態指定 docker-container port 的需求。另外一個點則是我還是比較喜歡在 xUnit 裡使用非同步的方法來執行,但是 FluentDocker 目前在我所使用的功能裡都還沒有提供非同步方法,於是我就混合著 FluentDocker 與 Testcontainers 分別負責建立 docker-image、docker-container 的工作。
不過腦筋動得快的人應該就可以想到,既然是想要在建立 docker-container 時可以去指定 port 的話,那麼只需要在執行 docker-compose 前去修改 docker-compose.yml 內容,將程式執行時隨機產生的 port 覆寫到 docker-compose.yml 不就好了…
沒錯,就是想要這麼做。說穿了也沒有什麼高深刁鑽的技巧,就只是有沒有想到與要不要這麼做而已。
docker-compose.yml 檔案
先準備好 docker-compose.yml 文件,把要執行的 container 設定都寫好,其中使用到的 mongo_Dockerfile 檔案內容可以查看上一篇文章
其中 ports 的內容 , 就是等一下要在執行時去修改的地方。
修改後的 MongoFixture.cs 程式內容
因為不會執行非同步方法,所以原本所繼承的 IAsyncLifetime 介面和兩個實做方法就需要移除,將建立 docker-image 與 docker-container 的部分就會在建構式裡執行,而當所有測試案例都執行完畢要結束測試專案時需要結束 docker-container 的部分,MongoFixture 繼承實做 IDosposable 介面,將移除 docker-container 的部分在 Dispose 方法裡執行。
以下就是修改後的 MongoFixture.cs 程式內容
using Ductus.FluentDocker.Builders;
using Ductus.FluentDocker.Services;
using Sample.Repositories.Infrastructure;
using FluentAssertions;
namespace Sample.RepositoriesTests;
public class MongoFixture : IDisposable
{
private ICompositeService _compositeService;
public MongoFixture()
{
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._compositeService.Dispose();
this._compositeService = null;
}
/// <summary>
/// 建立測試用的 MongoDB Docker-image 與 Docker-Container.
/// </summary>
private void CreateMongoDockerContainer()
{
ProjectFixture.DatabaseIp = "127.0.0.1";
ProjectFixture.DatabasePort = GetRandomPort();
var dockerComposeFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Docker", "Mongo", "docker-compose.yml");
var fileExists = File.Exists(dockerComposeFilePath);
if (fileExists is false)
{
throw new FileNotFoundException(message: "file not found.", fileName: dockerComposeFilePath);
}
var dockerComposeContent = File.ReadAllText(dockerComposeFilePath);
const string portString = "- 27017:27017";
var newPortString = $"- {ProjectFixture.DatabasePort}:27017";
dockerComposeContent = dockerComposeContent.Replace(portString, newPortString);
File.WriteAllText(dockerComposeFilePath, dockerComposeContent);
// 使用 FluentDocker (https://github.com/mariotoffia/FluentDocker)
// 透過 FluentDocker 的 UseCompose 功能執行 docker-compose.yml
// 執行 docker-compose 就會建立 MongoDB docker-image 與 docker-container
this._compositeService = new Builder().UseContainer().UseCompose().FromFile(dockerComposeFilePath).RemoveOrphans().Build().Start();
}
/// <summary>
/// Get Random Port
/// </summary>
/// <returns></returns>
private static ushort GetRandomPort()
{
var rnd = new Random();
var result = rnd.Next(49152, 65535);
return (ushort)result;
}
/// <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;
});
}
}
應該不需要對上面的程式內容多加說明吧!
完全只有用到 FluentDocker 而已,沒有使用到 Testcontainers。
FluentDocker 所提供的功能不會比 Testcontainers 來得少,而且多了很多設定的功能,適合想要在執行時需要動手去做各種設定的開發人員,例如 Networking、Volume、Logging、Docker Compose 等等
Testcontainers 與 FluentDocker 都是好用的工具,沒有說一定要用哪一種,就依據自己的習慣、喜好來決定,或者混著用也可以。
Testcontainers
FluentDocker
- https://github.com/mariotoffia/FluentDocker
- .NET Integration Testing with Docker Compose | Gui Ferreira - Minimalist Software Craftsman
Youtube
純粹是在寫興趣的,用寫程式、寫文章來抒解工作壓力