1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

.Net Framework4.8でTestContainersチュートリアルを試す

Last updated at Posted at 2025-06-07

概要

.Net FrameworkでTestContainersを導入したので防備録を残しておきます!

TestContainersとは?

Testcontainersは、Dockerコンテナで実行できるデータベースなどのソフトウェアに関して軽量で使い捨て可能なインスタンスを提供するためのオープンソースフレームワークです。

このフレームワークを使うと、運用環境と同等のデータベースインスタンスをテスト環境で何度でも簡単に再現し利用できます。

TestContainersで実現したいこと

データベースを含めた結合テストを効率化すること。

開発環境

名称 バージョン 説明
Windows 10 OS
VisualStudio Community 2022 IDE
.Net FrameWork 4.8 WindowsFormのフレームワーク
Docker Desktop for windows DockerのGUI版

NuGet関係のパッケージ

名称 バージョン 説明
TestContainers 3.3.0 データアクセス層のテストコンテナライブラリ
TestContainers.PostgreSql 3.3.0 TestContainersのPostgresqlインスタンス
xUnit 2.9.3 C#のユニットテストのフレームワーク
xunit.runner.msbuild 2.9.3 xUnitの実行環境
xunit.runner.visualstudio 3.1.0 xUnitの実行環境(visual studioのテストエクスプローラー)
Npgsql 8.0.6 Postgresqlにアクセスするためのライブラリ
Docker.DotNet 3.125.15 .NetのDocker用ライブラリ(TestContainerを導入すると自動的に追加される)
Docker.DotNet.X509 3.125.15 .NetのDocker用ライブラリ(TestContainerを導入すると自動的に追加される)

手順

Nugetでパッケージをインストールする。

NuGet関係のパッケージをインストールする

NugetのDocker.DotNetをアセンブルバインディング

TestContainersが要求するDocker.DotNetのバージョンが3.128.00なのでバージョンの不一致が発生します。
そこでアセンブルバインディグで参照するDocker.DotNetのバージョンを変更します。
(ここが一番手間取りました。。。。。。。。。。。)

App.config
      <dependentAssembly>
        <assemblyIdentity name="Docker.DotNet" publicKeyToken="e628c633e2cf4146" culture="neutral" />
-        <bindingRedirect oldVersion="3.125.0.0" newVersion="3.125.0.0" />
+        <bindingRedirect oldVersion="0.0.0.0-3.125.0.0" newVersion="3.125.0.0" />
      </dependentAssembly>

      <dependentAssembly>
        <assemblyIdentity name="Docker.DotNet.X509" publicKeyToken="e628c633e2cf4146" culture="neutral" />
-        <bindingRedirect oldVersion="3.125.0.0" newVersion="3.125.0.0" />
+        <bindingRedirect oldVersion="0.0.0.0-3.125.0.0" newVersion="3.125.0.0" />
      </dependentAssembly>


テスト用のクラスを作成する

https://testcontainers.com/guides/getting-started-with-testcontainers-for-dotnet/
にならってクラスを作成する

フォルダ構造
image.png

Customer.cs

Customer.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WindowsForm_sample
{
    // Fix for CS8370: Replace 'record struct' with 'struct' as C# 7.3 does not support 'record struct'.  
    // Fix for CS9113: Ensure the parameters are used in the struct.  

    public struct Customer
    {
        public long Id { get; }
        public string Name { get; }

        public Customer(long id, string name)
        {
            Id = id;
            Name = name;
        }
    }
}

DbConnectionProvider

DbConnectionProvider.cs
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Npgsql;

namespace WindowsForm_sample
{
    public sealed class DbConnectionProvider
    {
        private readonly string _connectionString;

        public DbConnectionProvider(string connectionString)
        {
            _connectionString = connectionString;
        }

        public DbConnection GetConnection()
        {
            return new NpgsqlConnection(_connectionString);
        }
    }
}

CustomerService

CustomerService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.Common;
using Npgsql;


namespace WindowsForm_sample
{
    public 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";
                    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);
                    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))";
                    connection.Open();
                    command.ExecuteNonQuery();
                }
            }
        }
    }
}


CustomerServiceTest

CustomerServiceTest.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;
using Testcontainers.PostgreSql;

namespace WindowsForm_sample
{
    public class CustomerServiceTest : IAsyncLifetime
    {
        public 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());
        }
    }
}


結果

実行中の様子

テスト中の画面
dockerコンテナが立ち上がっていることを確認できます
image.png

テスト完了後の画面
image.png

エラーの場合

Assertionの取得件数を変更すると テスト結果はNGになります。
image.png

image.png

課題

・SQLServerで試せるか
・Dapperでもテストできるか
・github Action CIする

参考

.NET用 Testcontainersを使い始める

TestContainers .Net Framework4.x の設定方法

githubコミット分

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?