.NET で Testcontainers を使い始める
コードを入手する
Testcontainers は、Docker コンテナでラップされた実際のサービスを使用して統合テストを簡単かつ軽量な API で構築できるテストライブラリです。Testcontainers を使用すると、本番環境で使用しているサービスと同じ種類のサービスと対話するテストを、モックやインメモリサービスを使用せずに記述できます。
Testcontainers を初めて使用する場合は、Testcontainers とは何か、なぜ使うべきなのか?を読んで、Testcontainersについて詳しく学んでください。
Postgres データベースを使用して .NET アプリケーションをテストするために、Testcontainers をどのように活用できるかを見ていきましょう。
ソリューションファイルを作成し、ソースプロジェクトとテストプロジェクトを準備する
ターミナルまたはお好みの IDE を使用して、.NET のソースプロジェクトとテストプロジェクトを作成します。
$ dotnet new sln -o TestcontainersDemo
$ cd TestcontainersDemo
$ dotnet new classlib -o CustomerService
$ dotnet sln add ./CustomerService/CustomerService.csproj
$ dotnet new xunit -o CustomerService.Tests
$ dotnet sln add ./CustomerService.Tests/CustomerService.Tests.csproj
$ dotnet add ./CustomerService.Tests/CustomerService.Tests.csproj reference ./CustomerService/CustomerService.csproj
プロジェクトが作成されたら、ソースプロジェクトに Npgsql の依存関係を以下のように追加します:
dotnet add ./CustomerService/CustomerService.csproj package Npgsql
これで、ソースプロジェクトとテストプロジェクトには以下の依存関係が含まれているはずです。
ソースプロジェクト
<PackageReference Include="Npgsql" Version="7.0.4" />
テストプロジェクト
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"/>
Postgres データベースとやり取りするために、Postgres 用の ADO.NET データプロバイダーである Npgsql を追加しました。また、サービスのテストには xUnit を使用します。
ビジネスロジックの実装
顧客情報を管理するための CustomerService
クラスを作成します。
まず、以下のように Customer
型を作成します:
namespace Customers;
public readonly record struct Customer(long Id, string Name);
ADO.NET の接続パラメータ(接続文字列)を保持するための DbConnectionProvider
クラスを作成し、データベース接続を取得するメソッドを以下のように実装します:
using System.Data.Common;
using Npgsql;
namespace Customers;
public sealed class DbConnectionProvider
{
private readonly string _connectionString;
public DbConnectionProvider(string connectionString)
{
_connectionString = connectionString;
}
public DbConnection GetConnection()
{
return new NpgsqlConnection(_connectionString);
}
}
CustomerService
クラスを作成し、以下のコードを追加してください:
namespace Customers;
public sealed class CustomerService
{
private readonly DbConnectionProvider _dbConnectionProvider;
public CustomerService(DbConnectionProvider dbConnectionProvider)
{
_dbConnectionProvider = dbConnectionProvider;
CreateCustomersTable();
}
public IEnumerable<Customer> GetCustomers()
{
IList<Customer> customers = new List<Customer>();
using var connection = _dbConnectionProvider.GetConnection();
using var command = connection.CreateCommand();
command.CommandText = "SELECT id, name FROM customers";
command.Connection?.Open();
using var dataReader = command.ExecuteReader();
while (dataReader.Read())
{
var id = dataReader.GetInt64(0);
var name = dataReader.GetString(1);
customers.Add(new Customer(id, name));
}
return customers;
}
public void Create(Customer customer)
{
using var connection = _dbConnectionProvider.GetConnection();
using var command = connection.CreateCommand();
var id = command.CreateParameter();
id.ParameterName = "@id";
id.Value = customer.Id;
var name = command.CreateParameter();
name.ParameterName = "@name";
name.Value = customer.Name;
command.CommandText = "INSERT INTO customers (id, name) VALUES(@id, @name)";
command.Parameters.Add(id);
command.Parameters.Add(name);
command.Connection?.Open();
command.ExecuteNonQuery();
}
private void CreateCustomersTable()
{
using var connection = _dbConnectionProvider.GetConnection();
using var command = connection.CreateCommand();
command.CommandText = "CREATE TABLE IF NOT EXISTS customers (id BIGINT NOT NULL, name VARCHAR NOT NULL, PRIMARY KEY (id))";
command.Connection?.Open();
command.ExecuteNonQuery();
}
}
CustomerService
クラスでは、顧客情報を管理するためのいくつかのメソッドが実装されています。それぞれの動作を以下に説明します。
-
_dbConnectionProvider.GetConnection()
: ADO.NET を使用してデータベース接続を取得します。 -
CreateCustomersTable()
メソッド:customers
テーブルが存在しない場合に作成します。 -
GetCustomers()
メソッド:customers
テーブルからすべての行を取得し、それをCustomer
オブジェクトに変換してリストとして返します。 -
Create(Customer)
メソッド: 新しい顧客レコードをデータベースに挿入します。
次に、Testcontainers を活用して、CustomerService
のロジックをどのようにテストできるかを見ていきましょう。
Testcontainers の依存関係を追加
Testcontainers を使用したテストを書く前に、テストプロジェクトに Testcontainers の PostgreSQL モジュール依存関係を以下のように追加します:
dotnet add ./CustomerService.Tests/CustomerService.Tests.csproj package Testcontainers.PostgreSql
これで、テストプロジェクトに Testcontainers.PostgreSql の依存関係が以下のように追加されているはずです:
私たちのアプリケーションでは Postgres データベースを使用しているため、テスト依存関係として Testcontainers の Postgres モジュール を追加しました。
Testcontainers を使用したテストの作成
テストプロジェクトに CustomerServiceTest
クラスを作成し、以下のコードを追加してください:
using Testcontainers.PostgreSql;
namespace Customers.Tests;
public sealed class CustomerServiceTest : IAsyncLifetime
{
private readonly PostgreSqlContainer _postgres = new PostgreSqlBuilder()
.WithImage("postgres:15-alpine")
.Build();
public Task InitializeAsync()
{
return _postgres.StartAsync();
}
public Task DisposeAsync()
{
return _postgres.DisposeAsync().AsTask();
}
[Fact]
public void ShouldReturnTwoCustomers()
{
// Given
var customerService = new CustomerService(new DbConnectionProvider(_postgres.GetConnectionString()));
// When
customerService.Create(new Customer(1, "George"));
customerService.Create(new Customer(2, "John"));
var customers = customerService.GetCustomers();
// Then
Assert.Equal(2, customers.Count());
}
}
CustomerServiceTest
クラスのコードを理解していきましょう。
-
PostgreSqlContainer の宣言: Postgres ビルダーに Docker イメージ名
postgres:15-alpine
を渡してPostgreSqlContainer
を宣言します。 -
Postgres コンテナの起動: Postgres コンテナは、
xUnit.net
のIAsyncLifetime
インターフェースを使用して起動されます。このインターフェースは、テストクラスが作成された直後にInitializeAsync
メソッドを実行します。 -
ShouldReturnTwoCustomers() テスト
CustomerService
を初期化します。- データベースに 2 件の顧客レコードを挿入します。
- 顧客テーブルのすべての行を取得し、顧客数が期待通りであることを検証します。
-
Postgres コンテナの破棄: テストメソッドの実行後に
DisposeAsync()
メンバーが実行され、Postgres コンテナが破棄されます。
以下のコマンドを使用してテストを実行します:
dotnet test
CustomerService
テストを実行すると、出力に次の動作が表示されます:
- Testcontainers が DockerHub から Postgres Docker イメージを取得(ローカルにイメージがない場合)
- コンテナの起動
- テストの実行
これで、Testcontainers を使用した最初のテストが実行されました!
まとめ
Testcontainers for .NET ライブラリを使用して、Postgres データベースを用いた .NET アプリケーションのテスト方法を学びました。
Testcontainers を使用した統合テストの記述方法は、IDE から実行できる単体テストの記述方法と非常に似ています。また、チームメンバーはプロジェクトをクローンし、自分のコンピュータに Postgres をインストールせずにテストを実行できます。
Postgres 以外にも、Testcontainers は多くの一般的に使用される SQL データベース、NoSQL データベース、メッセージングキューなどの専用モジュールを提供しています。Testcontainers を使用して、テストのためにあらゆるコンテナ化された依存関係を実行できます!
Testcontainers の詳細については、https://testcontainers.com をご覧ください。