目的
Usecaseに対して初期化したDBをTestContainer for .Netで用意しUnitTestをする
環境
- .Net6
- Docker
- docker-compose.yml
- EntityFrameworkCore
- Postgres
- TestContainer for .Net
- xunit
上記を利用します
TestContainer for .Net
アプリ上でContainerの起動&停止を行ってくれます。今回はPostgresのコンテナの起動&停止を行います。
TestContainerはContainerのPullは行ってくれないので、事前にPullしておく必要があります。
docker pull testcontainers/ryuk:0.3.4
Postgres
TestContainer経由でPostgresを立ち上げますが、こちらもPullしてくれないので、事前にPullしましょう
docker pull postgres:14.4
成果物
リポジトリ
にコードがあるので参照してください。
このコードは
- UnitTest開始
- TestContainerでPostgresから作成(テストクラス毎)
- テーブル作成
- 初期データを投入
- テスト
- DB削除
です
テーブルと初期データ
Postgres起動時にinit.sqlを実行します。init.sqlはpostgresql\initにあり
/docker-entrypoint-initdb.dにマウントすることによりユーザ作成、テーブル作成、初期データ投入します
スキーマや初期データに変更がある場合はinit.sqlを書き換えてください。
テストクラス
public class UserUsecaseTest : IClassFixture<TestBase>
TestBaseというクラスにTestContainerでPostgres起動するロジックを記載しています
UserUsecaseTestはIClassFixtureを継承しており、クラスの初期化でTestBaseを呼ぶことになります。
public class TestBase : IDisposable
{
public TestBase()
{
//EntityFrameworkCoreの設定、TestContainerで立ち上げたPostgresに接続
_services.AddDbContext<SampleDbContext>(Options =>
{
Options.UseNpgsql(UnitTestConnectionString);
});
//UnitTestで使うサービスをDIに登録
_services.AddScoped<IUserRepository, UserRepository>();
_services.AddScoped<IUserUsecase, UserUsecase>();
ServiceProvider = _services.BuildServiceProvider();
//TestContainer起動
Initialize();
}
public void Dispose()
{
//Postgresの停止&削除
Task.Run(_postgresqlContainer!.DisposeAsync).GetAwaiter().GetResult();
}
}
TestBaseのコンストラクタでDIの設定、TestContainer起動
Disposeで後始末を行っています。
詳細はコードを参照してください
[assembly: CollectionBehavior(DisableTestParallelization = true)]
でテストは直列で行うようにしています。これは並列にするとDBが複数立ち上がりリソース圧迫を防ぐ目的です。
おまけ
UT/docker-compose.ymlを置いているので
docker compose up
でUnitTestで使用するDBと同じものを違うContainerで立ち上げることができます。
デバッグや開発ではこちらを使い、スキーマや初期データがそろったら、init.sqlを変更しUnitTestを行うというフローが出来ます
その場合
Database名:test
ユーザ:user
パスワード:pass
docker-compose.ymlに記載しているので適宜変更してください
感想
DBをテストクラス毎に起動&停止を行うので試験に時間はかかりますが、本番環境とは違うDB、例えばInMemoryやSQLiteなどを利用することなく、本番と同環境でUnitTestが出来ることに意義があると思っています。