多くの場合、プロジェクトは特定のテクノロジに非常に反復的な方法で依存しており、そのテクノロジと対話するためにコードを 1 つのプロジェクトから別のプロジェクトに移動する状況に陥る可能性があります。 「このコードをライブラリにパッケージ化して、どこでも利用してみてはどうだろうか」
そのとき、モジュール性、デカップリング、凝集性など、特定のソフトウェアエンジニアリングのベストプラクティスを思い出すのです。
このブログ記事では、 Testcontainers for Goの新しいモジュールを作成し、プロジェクトで使用するテクノロジーと、コードをテストするためのテクノロジーを表す方法について説明します。
モジュールを最初から作成する
Testcontainers for Go モジュールは、特定のテクノロジの API を公開する Go モジュールにすぎません。 Go モジュールをゼロから簡単に作成できるように、Testcontainers for Go には、関心のあるモジュールのコードのスキャフォールディングを生成するコマンドライン ツールが用意されているため、すぐに開始できます。
このツールは以下を生成します。
- このテクノロジーのGoモジュールには、次のものが含まれます。
go.mod
Go用のTestcontainersの現在のバージョンを含むgo.sum
ファイル。- モジュールにちなんで名付けられた小文字の Go パッケージ。
- コンテナを作成するための Go ファイルで、イメージ フラグの値が Docker イメージとして使用される専用の構造体を使用します。
- コンテナの簡単なテストを実行するためのGoテストファイルで、上記の構造体を消費します。
- 一貫した方法でテストを実行するための Makefile。
docs/modules
ディレクトリ内のマークダウンファイルには、コンテナの作成と簡単なテストの両方のスニペットが含まれています。デフォルトでは、この生成されたファイルには、以下を含むモジュールのすべてのドキュメントが含まれます。- モジュールが追加された Testcontainers for Go のバージョン。
- モジュールの簡単な紹介です。
- プロジェクトの依存関係にモジュールを追加するためのセクション。
- Go モジュールの
examples_test.go
ファイルからコンテナを作成するためのスニペットを含む、使用例のセクション。 - コンテナを作成するためのエントリポイント関数や、コンテナを作成するためのオプションなど、モジュール参照のセクション。
- コンテナメソッドのセクションです。
- ドキュメント サイト内のモジュールの新しい Nav エントリで、プロジェクトのルート ディレクトリにある
mkdocs.yml
ファイルに追加します。 .github/workflows
内の GitHub ワークフロー ファイル ディレクトリに移動して、テクノロジのテストを実行します。- プロジェクトのワークスペースに新しいモデルを含めるための VSCode ワークスペース ファイル内のエントリ。
コード生成ツール
コード生成ツールは、Goの依存関係がTestcontainers for Goに分散されるのを避けるために、Goモジュールとして modulegen
ディレクトリに存在するGoプログラムであり、その唯一の目的は、Goテンプレートを使用してGoモジュールのスキャフォールディングを作成することです。
コード生成ツールのコマンドライン フラグを表 1に示します。
旗 | 種類 | 必須 | 形容 |
–名前 | 糸 | はい | モジュールの名前。必要に応じてキャメルケースを使用してください。 英数字のみを使用できます (先頭の文字は文字である必要があります)。 |
–画像 | 糸 | はい | モジュールが使用する Docker イメージの完全修飾名 ( docker.io/org/project:tag ) |
–タイトル | 糸 | いいえ | 大文字と小文字の混在をサポートする名前のバリアント (MongoDB など)。 英数字のみを使用できます (先頭の文字は文字である必要があります)。 |
モジュール名またはタイトルに英数字が含まれていない場合、プログラムは生成を終了します。 また、モジュールがすでに存在する場合は、既存のファイルを更新せずに終了します。
modulegen
ディレクトリから、次のコマンドを実行します。
go run . new module --name ${NAME_OF_YOUR_MODULE} --image "${REGISTRY}/${MODULE}:${TAG}" --title ${TITLE_OF_YOUR_MODULE}
モジュールへの型とメソッドの追加
モジュールに型とメソッドを追加する際に従うべき一連の手順を提案します。
- モジュールにパブリック
Container
タイプが存在することを確認します。 このタイプは、testcontainers.Container
タイプを埋め込むためにコンポジションを使用し、そこからすべての方法を促進する必要があります。 RunContainer
関数が存在し、パブリックであることを確認してください。この関数はモジュールへのエントリポイントであり、イメージ、デフォルトの公開ポート、待機戦略など、testcontainers.GenericContainerRequest
構造体の初期値を定義します。 その結果、関数はコンテナリクエストをデフォルト値で初期化する必要があります。- モジュールのコンテナオプションを定義するには、
testcontainers.ContainerCustomizer
インターフェースを使用します。このインターフェースにはCustomize(req *GenericContainerRequest) error
つの方法があります。
手記: オプションのベスト プラクティスは、With プレフィックスを使用して関数を定義し、変更された testcontainers.GenericContainerRequest
型を返す関数を返すことであると考えています。 そのため、ライブラリにはContainerCustomizer
インターフェイスを実装するtestcontainers.CustomizeRequestOption
型がすでに用意されているため、この型を使用して独自のカスタマイザー関数を作成することをお勧めします。
- 同時に、モジュール用に独自のコンテナカスタマイザーを作成する必要がある場合があります。
testcontainers.ContainerCustomizer
インターフェースを実装していることを確認します。独自のカスタマイザー関数を定義することは、コンテナのContainerRequest
に存在しない特定の状態を転送する必要がある場合や、場合によっては中間の Config 構造体を使用する必要がある場合に便利です。 以下のスニペットでMyCustomizer
とWithMy
を見てみましょう。 - オプションは、Go コンテキストの後に可変個引数として
RunContainer
関数に渡され、for ループを使用して最初のtestcontainers.GenericContainerRequest
構造体を定義した直後に処理されます。 - 必要に応じて、
Container
型をレシーバーとして使用して、実行中のコンテナから情報を抽出するパブリックメソッドを定義します。 たとえば、データベースにアクセスするための接続文字列が役立つ方法として考えられます。 コンテナメソッドを追加する際には、ユーザーにとって何が役立つかを考えてください。 - 公開APIをGoコメントで文書化します。
- モジュールの新しい API を説明するためにドキュメントを拡張します。 コード生成ツールでは、
Container options
サブセクションとContainer methods
サブセクションを含む親Module reference
セクションがすでに作成されています。各サブセクション内で、各オプションとメソッドのネストされたサブセクションをそれぞれ定義してください。
次のスニペットは、コードがどのように見えるかの例を示しています。
type ModuleContainer struct {
testcontainers.Container
cfg *Config
}
// Config type represents an intermediate struct for transferring state from the options to the container
type Config struct {
data string
}
// RunContainer is the entrypoint to the module
func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomizer) (*Container, error) {
cfg := Config{}
req := testcontainers.ContainerRequest{
Image: "my-image",
//...
}
genericContainerReq := testcontainers.GenericContainerRequest{
ContainerRequest: req,
Started: true,
}
//...
for _, opt := range opts {
if err := opt.Customize(&genericContainerReq); err != nil {
return nil, err
}
// If you need to transfer some state from the options to the container, you can do it here
if myCustomizer, ok := opt.(MyCustomizer); ok {
config.data = customizer.data
}
}
//...
container, err := testcontainers.GenericContainer(ctx, genericContainerReq)
//...
moduleContainer := &Container{Container: container}
// use config.data here to initialize the state in the running container
//...
return moduleContainer, nil
}
// MyCustomizer type represents a container customizer for transferring state from the options to the container
type MyCustomizer struct {
data string
}
// Customize method implementation
func (c MyCustomizer) Customize(req *testcontainers.GenericContainerRequest) testcontainers.ContainerRequest {
req.ExposedPorts = append(req.ExposedPorts, "1234/tcp")
return req.ContainerRequest
}
// WithMy function option to use the customizer
func WithMy(data string) testcontainers.ContainerCustomizer {
return MyCustomizer{data: data}
}
// WithSomeState function option leveraging the functional option
func WithSomeState(value string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) {
req.Env["MY_ENV_VAR"] = value
}
}
// ConnectionString returns the connection to the module container
func (c *Container) ConnectionString(ctx context.Context) (string, error) {...}
ContainerRequest オプション
特定のモジュールのコンテナの作成を簡略化するために、Testcontainers for Go には、モジュールのコンテナリクエストをカスタマイズするための一連の testcontainers.CustomizeRequestOption
関数が用意されています。 これにより、サービスのコンテナリクエストを完全にカスタマイズできます。
これらのオプションは次のとおりです。
testcontainers.WithImage
: コンテナリクエストのイメージを設定する関数。testcontainers.WithImageSubstitutors
: コンテナイメージに独自の置換を設定する関数。testcontainers.WithEnv
: コンテナリクエストの環境変数を設定する関数。testcontainers.WithHostPortAccess
: ホストで既に実行されているポートにコンテナがアクセスできるようにする関数。testcontainers.WithLogConsumers
: コンテナリクエストのログコンシューマを設定する関数。testcontainers.WithLogger
: コンテナリクエストのロガーを設定する関数。testcontainers.WithWaitStrategy
: コンテナリクエストの待機ストラテジーを設定する関数で、渡されたすべての待機ストラテジーをコンテナリクエストに追加し、期限が 60 秒のtestcontainers.MultiStrategy
を使用します。詳細については 、待機戦略 を参照してください。testcontainers.WithWaitStrategyAndDeadline
: 渡された期限を持つtestcontainers.MultiStrategy
を使用して、渡されたすべての待機ストラテジーをコンテナリクエストに追加し、コンテナリクエストの待機ストラテジーを設定する関数。 詳細については 、待機戦略 を参照してください。testcontainers.WithStartupCommand
: コンテナの起動時にコマンドの実行を設定する関数。testcontainers.WithAfterReadyCommand
: コンテナの準備ができた (待機戦略が満たされている) 直後にコマンドの実行を設定する関数。testcontainers.WithNetwork
: コンテナリクエストのネットワークとネットワークエイリアスを設定する関数。testcontainers.WithNewNetwork
: コンテナリクエストの使い捨てネットワークのネットワークエイリアスを設定する関数。testcontainers.WithConfigModifier
: コンテナリクエストの設定Dockerタイプを設定する関数。 詳細については、「 詳細設定 」を参照してください。testcontainers.WithEndpointSettingsModifier
: コンテナリクエストのエンドポイント設定の Docker タイプを設定する関数。 詳細については、「 詳細設定 」を参照してください。testcontainers.WithHostConfigModifier
: コンテナリクエストのホスト設定 Docker タイプを設定する関数。 詳細については、「 詳細設定 」を参照してください。testcontainers.CustomizeRequest
: デフォルトのtestcontainers.GenericContainerRequest
をユーザーが提供するものとマージする関数。 コンテナリクエストを完全にカスタマイズする場合に推奨されます。
それらのいずれかを使用して、 RunContainer
関数を呼び出すときにコンテナをカスタマイズできます。 LocalStack モジュールの例を次に示します。
container, err := localstack.RunContainer(ctx,
testcontainers.WithImage("localstack/localstack:2.0.0"),
testcontainers.WithEnv(map[string]string{"SERVICES": "s3,sqs"}),
)
結論
この記事では、Testcontainers for Go の Go モジュールを作成し、カスタム型、メソッド、カスタマイザーを追加する方法を共有しました。 最後に、コード生成ツールを使用して新しいモジュールのスキャフォールディングを作成する方法を発見しました。
モジュールについてもっと知りたいですか? 次のリンクを確認してください。
さらに詳しく
- Docker Newsletter を購読してください。
- 無料アカウントを作成して、Testcontainers Cloud の使用を開始してください。
- Testcontainers Slack で接続します。
- Testcontainers のベスト プラクティスについて説明します。
- Testcontainers ガイドから開始します。