ホームページ  >  記事  >  バックエンド開発  >  testcontainers-go と docker-compose でテスト スイートを活用する

testcontainers-go と docker-compose でテスト スイートを活用する

Linda Hamilton
Linda Hamiltonオリジナル
2024-10-04 16:07:02170ブラウズ

Leverage Your Test Suite With testcontainers-go & docker-compose

皆さん、おかえりなさい!今日は、興味深いブログ投稿でエンドツーエンドのテストについて取り上げます。このような種類のテストを書いたことがない場合、またはテストを改善しようと努力している場合は、このエキサイティングな旅を説明するので読み続けてください。この記事を最後まで読むと、testcontainers-go パッケージを使用してテスト スイートを有効に活用する方法がわかります。

前提?

先に進む前に、このブログ投稿の境界線を設定しましょう。いくつかの概念、ツール、テクニックについて説明するためです。

サバイバルリスト?️

このブログ投稿の残りの部分でいくつかのトピックに触れることになるので、ここでそれらをまとめておくのが良いと思います。

このブログ投稿全体で私が紹介するツールには、私がよく知っているツールと初めて使用したツールが混在しています。これらのツールをむやみに使用するのではなく、シナリオに基づいて評価してください。

私たちは以下に依存します:

  • Go プログラミング言語
  • ドッカー
  • compose モジュールを含む testcontainers-go パッケージ
  • ginkgo テスト フレームワークと gomega アサーション パッケージ

読み物が膨大になるのを避けるため、ここで紹介するトピックのあらゆる側面や側面を取り上げるつもりはありません。必要に応じて、関連ドキュメントの URL を記載します。

シナリオ?

私たちが所有していないプロジェクトに対してエンドツーエンドのテストを作成する必要があると仮定しましょう。私の場合、Java プログラミング言語で書かれたプロジェクトに対してエンドツーエンドのテストを書きたいと考えています。私は Java でコーディングする方法を知らなかったので、テストのオプションはエンドツーエンドのテストのみでした。私がテストしなければならなかったサービスは、REST API のセットでした。解決策は明らかです。HTTP リクエストを発行してエンドポイントを実行します。

ブラック ボックスのように公開された機能をテストできます。処理する必要があるのは、サーバーに何を送信し、サーバーから何を取得するかという公開表面だけです。それ以上でもそれ以下でもありません。

私たちは、データベース (MySQL インスタンス) 内の銀行口座をリストする API/accounts エンドポイントに注目します。次の 2 つのリクエストを発行します:

HTTP Method Address Expected Status Code
GET api/accounts?iban=IT10474608000005006107XXXXX 200 StatusOK
GET api/accounts?iban=abc 400 StatusBadRequest

これで、私たちの目標がより明確になったはずです。それでは、テストコードに移りましょう。

楽しみましょう?

このセクションでは、恐ろしいエンドツーエンド テストのために記述する必要がある関連コードをすべて示します。

docker-compose.yml ファイル

ソース コードにこだわる必要はないので、開始点は docker-compose.yml ファイルです。関連するコードは次のとおりです:


services:
  mysqldb:
    image: "mysql:8.0"
    container_name: mysqldb
    restart: always
    ports:
      - 3307:3306
    networks:
      - springapimysql-net
    environment:
      MYSQL_DATABASE: transfers_db
      MYSQL_USER: bulk_user
      MYSQL_PASSWORD: root
      MYSQL_ROOT_PASSWORD: root
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s

  api_service:
    build: .
    container_name: api_service
    restart: always
    ports:
      - 8080:8080
    networks:
      - springapimysql-net
    environment:
      - spring.datasource.url=jdbc:mysql://mysqldb:3306/transfers_db
      - spring.datasource.username=bulk_user
      - spring.datasource.password=root
    depends_on:
      mysqldb:
        condition: service_healthy
    volumes:
      - .m2:/root/.m2

networks:
  springapimysql-net:



ファイルの内容は非常に単純です。次のリストで定義されている内容を要約できます:

  • mysqldb サービスについてはこれ以上説明する必要はありません
  • api_service サービスは私たちがテストしているシステムです
  • springapimysql-net ネットワークは、上で定義した 2 つのサービスをホストします

Docker Compose の詳細については、ここを参照してください。次に、エンドツーエンドのテスト コードを見てみましょう。

イチョウのテストフレームワーク

ginkgo テスト フレームワークは、テスト スイートの構築に役立ちます。すべて Go で書かれています。さらに、テストをセットアップして実行するための CLI ユーティリティも提供します。後で使用するので、ここからダウンロードしましょう。次の 2 つの方法でダウンロードできます:

  1. go install コマンドを使用する (システムに Go をインストールしている場合)
  2. コンパイルされたバイナリをダウンロードする (システムに Go がインストールされていない場合に役立ちます)

ご使用のマシン上で機能するユーティリティがあるかどうかを確認するには、コマンド ginkgo バージョンを実行できます (この記事の執筆時点では、バージョン 2.20.2 を使用しています)。

テストを実行するには、ginkgo コマンドは必須ではないことに注意してください。 go test コマンドを使用することで、このユーティリティがなくてもテストを実行できます。

ただし、定型コードの生成に使用するため、ダウンロードすることを強くお勧めします。

イチョウで基礎を築く

ルート ディレクトリに、テストをホストするための end2end というフォルダーを作成しましょう。そのフォルダー内で、コマンド go mod init path/to/your/module. を発行して、
Go モジュール
を初期化します。 次に、コマンド ginkgo bootstrap を実行します。 end2end_suite_test.go という新しいファイルが生成されるはずです。このファイルは、後で定義するテスト スイートをトリガーします。

このアプローチは、testify/suite パッケージのアプローチと似ています。定義フェーズと実行フェーズが分離されているため、コードのモジュール性と堅牢性が強化されます。

次に、スイートにテストを追加しましょう。テストが存在するファイルを生成するには、別の ginkgo コマンド (ginkgo generated accounts) を実行します。今度は、accounts_test.go ファイルが表示されます。とりあえずはそのままにしてターミナルに切り替えましょう。 Go コマンド go mod tiny を実行して、不足している依存関係をマシンにローカルにダウンロードすることで、不足しているパッケージを修正します。

end2end_suite_test.go ファイル

テスト スイートのエントリ ポイントから始めましょう。ファイルの内容はきれいに見えます:


//go:build integration

package end2end

import (
 "testing"

 . "github.com/onsi/ginkgo/v2"
 . "github.com/onsi/gomega"
)

func TestEnd2End(t *testing.T) {
 RegisterFailHandler(Fail)
 RunSpecs(t, "End2End Suite")
}



唯一の珍しい点は、import セクション内の dot-import かもしれません。詳細については、こちらのドキュメントをご覧ください。

おっと!野生のテストコンテナが現れる?

場合によっては、次のテスト レベルに進むために魔法が必要になります。それはたまたま testcontainers-go でした。このデモでは、compose モジュールを使用します (詳細については、こちらを参照してください)。

このツールは、前に見た構成ファイルを実行し、実行中のコンテナに対してエンドツーエンドのテストを実行できます。

これは testcontainers-go の機能の抜粋です。さらに詳しく知りたい場合は、ドキュメントを参照するか、お問い合わせください。その驚くべき機能について喜んでご案内させていただきます。

このパッケージを使用すると、単一のコマンドでエンドツーエンドのスイートを実行できます。これは、これらのテストを実行するためのより一貫性のあるアトミックな方法です。これにより次のことが可能になります:

  1. スイートの前にコンテナを起動します
  2. これらのコンテナに依存してテストを実行します
  3. スイート後のコンテナの破棄と使用されたリソースのクリーンアップ

この方法でコードを記述すると、docker cli コマンドや makefile を扱う手間を省くことができます。

accounts_test.go ファイル

次に、テストが存在するコードを見てみましょう。


//go:build integration

package end2end

import (
 "context"
 "net/http"
 "os"

 . "github.com/onsi/ginkgo/v2"
 . "github.com/onsi/gomega"
 tc "github.com/testcontainers/testcontainers-go/modules/compose"
 "github.com/testcontainers/testcontainers-go/wait"
)

var _ = Describe("accounts", Ordered, func() {
 BeforeAll(func() {
 os.Setenv("TESTCONTAINERS_RYUK_DISABLED", "true")
 composeReq, err := tc.NewDockerComposeWith(tc.WithStackFiles("../docker-compose.yml"))
  Expect(err).Should(BeNil())
  DeferCleanup(func() {
   Expect(composeReq.Down(context.Background(), tc.RemoveOrphans(true), tc.RemoveImagesLocal)).Should(BeNil())
  })
 ctx, cancel := context.WithCancel(context.Background())
  DeferCleanup(cancel)
 composeErr := composeReq.
   WaitForService("api_service", wait.ForListeningPort("8080/tcp")).
   Up(ctx, tc.Wait(true))
  Expect(composeErr).Should(BeNil())
 })

 Describe("retrieving accounts", func() {
  Context("HTTP request is valid", func() {
   It("return accounts", func() {
 client := http.Client{}
 r, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/api/accounts?iban=IT10474608000005006107XXXXX", nil)
 res, err := client.Do(r)
    Expect(err).Should(BeNil())
    Expect(res).To(HaveHTTPStatus(http.StatusOK))
   })
  })

  Context("HTTP request is NOT valid", func() {
   It("err with invalid IBAN", func() {
 client := http.Client{}
 r, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/api/accounts?iban=abcd", nil)
    Expect(err).Should(BeNil())
 res, err := client.Do(r)
    Expect(err).Should(BeNil())
    Expect(res).To(HaveHTTPStatus(http.StatusBadRequest))
   })
  })
 })
})



一見すると、理解するのが難しいように思えるかもしれません。作業を簡単にするために、小さな部分に分けてみましょう。

コンテナの説明ノード

Describe コンテナ ノードは、スイートに関連するコードを保持するラッパーにすぎません。すべてはその中に生きなければなりません。これはスキャフォールド コードの一部です: var _ = Describe("accounts", Ordered, func() {}。{} 内に関連するコードをすべて配置する必要があります。セットアップ ノード (BeforeAll と同様に)、Describe コンテナを Ordered として定義する必要があります。

これを追加するのを忘れても、Go コンパイラーが警告を発するため、心配する必要はありません。

次に進みましょう。

BeforeAll セットアップ ノード

このノードを使用すると、共通のセットアップ ロジックを抽出できます。このコード部分は、スイート内のテストの1 回に実行されます。何が行われているかを要約しましょう:

  • 環境変数 TESTCONTAINERS_RYUK_DISABLED を true に設定します。構成についてはここで学ぶことができます。リュークに興味のある方はこちらをご覧ください
  • 提供した docker-compose.yml ファイルに基づいて *tc.DockerCompose 変数を作成します
  • 関数の呼び出しを延期してコンテナーを終了し、リソースをクリーンアップします
  • 構成スタックを開始し、api_service というコンテナが起動して 8080/tcp ポートでリッスンする準備ができるまで待ちます

このブログ投稿をさらに長くしたくないので、テスト コードを簡略化しました?.

いよいよテストです! ?

テスト関数は Describe コンテナ ノード 内に存在します。 ginkgo がテスト仕様をどのように処理するかについては、ここを参照してください。 Describe ノードを使用すると、スコープに基づいてテストをグループ化および整理できます。このノードを他の Describe ノード内にネストできます。

Describe ノードをネストするほど、テスト範囲が狭くなります。

次に、親 Describe を修飾するコンテキスト コンテナ ノード があります。これにより、テストが有効となる状況が決まります。最後に、「It」セクション、つまり「Spec Subject」があります。これは私たちが実行している実際のテストであり、階層ツリーのリーフ レベルです。テスト コードは一目瞭然なので、テストを実行するセクションに進みます。

3、2、1…?

おめでとうございます?なんとかここにたどり着くことができました。今は、テスト実行操作だけを見逃しています。瞬く間に、テスト実行レポートが端末に印刷されます。

ターミナルに切り替えて、コマンド ginkgo --tags=integration -v を実行してみましょう。しばらくすると、出力がターミナルに表示されます。

締めくくりのメモ ?

このブログ投稿には多くのことが凝縮されていると思います。私の目標は、優れたテスト スイートを作成する方法についての洞察とアプローチを提供することです。提示されたツール、パッケージ、テクニックを他の種類のテストやユースケースに適応させたい場合があります。

終了する前に、testcontainers-go パッケージの compose モジュールのもう 1 つの美しさを強調しておきたいと思います。

私が提供した構成に固執すれば、最新の Docker イメージを使用することができ、古いイメージの使用による何時間ものトラブルシューティングを回避できます。これは、コマンド docker compose build --no-cache && docker compose up に似ています。感謝してくれるだろうか?

皆さん、ご清聴ありがとうございました!ご質問、疑問、フィードバック、コメントがございましたら、一緒に聞いたり話したりすることができます。特定の概念について説明してほしい場合は、私までご連絡ください。次回まで、気をつけてお会いしましょう?

以上がtestcontainers-go と docker-compose でテスト スイートを活用するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。