皆さん、おかえりなさい!今日は、興味深いブログ投稿でエンドツーエンドのテストについて取り上げます。このような種類のテストを書いたことがない場合、またはテストを改善しようと努力している場合は、このエキサイティングな旅を説明するので読み続けてください。この記事を最後まで読むと、testcontainers-go パッケージを使用してテスト スイートを有効に活用する方法がわかります。
先に進む前に、このブログ投稿の境界線を設定しましょう。いくつかの概念、ツール、テクニックについて説明するためです。
このブログ投稿の残りの部分でいくつかのトピックに触れることになるので、ここでそれらをまとめておくのが良いと思います。
このブログ投稿全体で私が紹介するツールには、私がよく知っているツールと初めて使用したツールが混在しています。これらのツールをむやみに使用するのではなく、シナリオに基づいて評価してください。
私たちは以下に依存します:
読み物が膨大になるのを避けるため、ここで紹介するトピックのあらゆる側面や側面を取り上げるつもりはありません。必要に応じて、関連ドキュメントの 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 ファイルです。関連するコードは次のとおりです:
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:
ファイルの内容は非常に単純です。次のリストで定義されている内容を要約できます:
Docker Compose の詳細については、ここを参照してください。次に、エンドツーエンドのテスト コードを見てみましょう。
ginkgo テスト フレームワークは、テスト スイートの構築に役立ちます。すべて Go で書かれています。さらに、テストをセットアップして実行するための CLI ユーティリティも提供します。後で使用するので、ここからダウンロードしましょう。次の 2 つの方法でダウンロードできます:
ご使用のマシン上で機能するユーティリティがあるかどうかを確認するには、コマンド 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 を実行して、不足している依存関係をマシンにローカルにダウンロードすることで、不足しているパッケージを修正します。
テスト スイートのエントリ ポイントから始めましょう。ファイルの内容はきれいに見えます:
//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 の機能の抜粋です。さらに詳しく知りたい場合は、ドキュメントを参照するか、お問い合わせください。その驚くべき機能について喜んでご案内させていただきます。
このパッケージを使用すると、単一のコマンドでエンドツーエンドのスイートを実行できます。これは、これらのテストを実行するためのより一貫性のあるアトミックな方法です。これにより次のことが可能になります:
この方法でコードを記述すると、docker cli コマンドや makefile を扱う手間を省くことができます。
次に、テストが存在するコードを見てみましょう。
//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 コンパイラーが警告を発するため、心配する必要はありません。
次に進みましょう。
このノードを使用すると、共通のセットアップ ロジックを抽出できます。このコード部分は、スイート内のテストの1 回と前に実行されます。何が行われているかを要約しましょう:
このブログ投稿をさらに長くしたくないので、テスト コードを簡略化しました?.
テスト関数は Describe コンテナ ノード 内に存在します。 ginkgo がテスト仕様をどのように処理するかについては、ここを参照してください。 Describe ノードを使用すると、スコープに基づいてテストをグループ化および整理できます。このノードを他の Describe ノード内にネストできます。
Describe ノードをネストするほど、テスト範囲が狭くなります。
次に、親 Describe を修飾するコンテキスト コンテナ ノード があります。これにより、テストが有効となる状況が決まります。最後に、「It」セクション、つまり「Spec Subject」があります。これは私たちが実行している実際のテストであり、階層ツリーのリーフ レベルです。テスト コードは一目瞭然なので、テストを実行するセクションに進みます。
おめでとうございます?なんとかここにたどり着くことができました。今は、テスト実行操作だけを見逃しています。瞬く間に、テスト実行レポートが端末に印刷されます。
ターミナルに切り替えて、コマンド ginkgo --tags=integration -v を実行してみましょう。しばらくすると、出力がターミナルに表示されます。
このブログ投稿には多くのことが凝縮されていると思います。私の目標は、優れたテスト スイートを作成する方法についての洞察とアプローチを提供することです。提示されたツール、パッケージ、テクニックを他の種類のテストやユースケースに適応させたい場合があります。
終了する前に、testcontainers-go パッケージの compose モジュールのもう 1 つの美しさを強調しておきたいと思います。
私が提供した構成に固執すれば、最新の Docker イメージを使用することができ、古いイメージの使用による何時間ものトラブルシューティングを回避できます。これは、コマンド docker compose build --no-cache && docker compose up に似ています。感謝してくれるだろうか?
皆さん、ご清聴ありがとうございました!ご質問、疑問、フィードバック、コメントがございましたら、一緒に聞いたり話したりすることができます。特定の概念について説明してほしい場合は、私までご連絡ください。次回まで、気をつけてお会いしましょう?
以上がtestcontainers-go と docker-compose でテスト スイートを活用するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。