Testcontainers for .NET 시작하기
Testcontainers for .NET 시작하기
Testcontainers for .NET과 실제 PostgreSQL 인스턴스로 .NET 어플리케이션을 만들고 데이터베이스 상호작용을 테스트하는 방법을 배워볼게요.
출처: 문서
본문
이 가이드에서 배울 내용:
- 소스 및 테스트 프로젝트가 있는 .NET 솔루션 만들기
- PostgreSQL에서 고객 레코드를 관리하는 CustomerService 구현하기
- Testcontainers와 xUnit으로 통합 테스트 작성하기
- IAsyncLifetime 으로 컨테이너 수명주기 관리하기
사전 요구사항
- .NET 8.0+ SDK
- Testcontainers가 지원하는 Docker 환경
참고: Testcontainers를 처음 접한다면 Testcontainers 개요 를 방문해 Testcontainers와 그 이점에 대해 알아보세요.
.NET 프로젝트 만들기
솔루션 설정
소스 및 테스트 프로젝트가 있는 .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
비즈니스 로직 구현
Customer 레코드 타입을 만들어주세요:
namespace Customers;
public readonly record struct Customer(long Id, string Name);
데이터베이스 연결을 관리하는 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 가 하는 일:
- 생성자가 CreateCustomersTable() 을 호출해 테이블이 존재하게 보장해요.
- GetCustomers() 가 customers 테이블의 모든 행을 가져와 Customer 객체로 반환해요.
- Create() 가 데이터베이스에 고객 레코드를 삽입해요.
Testcontainers로 테스트 작성하기
Testcontainers 의존성 추가
테스트 프로젝트에 Testcontainers PostgreSQL 모듈을 추가해주세요:
$ dotnet add ./CustomerService.Tests/CustomerService.Tests.csproj package Testcontainers.PostgreSql
테스트 작성
테스트 프로젝트에 CustomerServiceTest.cs 를 만들어주세요:
using Testcontainers.PostgreSql;
namespace Customers.Tests;
public sealed class CustomerServiceTest : IAsyncLifetime
{
private readonly PostgreSqlContainer _postgres =
new PostgreSqlBuilder()
.WithImage("postgres:16-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());
}
}
테스트가 하는 일:
- postgres:16-alpine Docker 이미지로 PostgreSqlBuilder 를 사용해 PostgreSqlContainer 를 선언해요.
- 컨테이너 수명주기 관리를 위해 IAsyncLifetime 을 구현해요:
- InitializeAsync() 가 테스트 실행 전에 컨테이너를 시작해요.
- DisposeAsync() 가 테스트 완료 후 컨테이너를 중지하고 제거해요.
- ShouldReturnTwoCustomers() 가 컨테이너의 연결 세부사항으로 CustomerService 를 만들고, 두 고객을 삽입하며, 모든 고객을 가져와 개수를 검증해요.
테스트 실행 및 다음 단계
테스트 실행
테스트를 실행해주세요:
$ dotnet test
출력에서 Testcontainers가 Postgres Docker 이미지를 Docker Hub에서 내려받고(로컬에 없을 경우), 컨테이너를 시작하며, 테스트를 실행하는 것을 볼 수 있어요.
Testcontainers로 통합 테스트를 작성하는 것은 IDE에서 실행할 수 있는 단위 테스트를 작성하는 것과 같아요. 팀원들이 Postgres를 자신의 머신에 설치하지 않고도 프로젝트를 클론해 테스트를 실행할 수 있어요.
요약
Testcontainers for .NET 라이브러리는 목(mock) 대신 프로덕션에서 사용하는 것과 같은 유형의 데이터베이스(Postgres)를 사용해 통합 테스트를 작성하게 도와줘요. 목을 사용하지 않고 실제 서비스와 대화하므로 코드를 자유롭게 리팩터링하면서도 어플리케이션이 예상대로 작동하는지 검증할 수 있어요.
Postgres 외에도 Testcontainers는 많은 SQL 데이터베이스, NoSQL 데이터베이스, 메시징 큐 등을 위한 전용 모듈 을 제공해요.
Testcontainers에 대해 더 배우려면 Testcontainers 개요 를 방문해주세요.
추가 자료
- ASP.NET Core 웹 앱 테스트
더 알아보기 (Learn more)
- Testcontainers 개요
- Testcontainers for .NET
- ASP.NET Core 앱 테스트