皆さん、おかえりなさい!今日は、興味深いブログ投稿でエンドツーエンドのテストについて取り上げます。このような種類のテストを書いたことがない場合、またはテストを改善しようと努力している場合は、このエキサイティングな旅を説明するので読み続けてください。この記事を最後まで読むと、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 |
Now, you should have a clearer idea of our goal. So, let's jump into the test code.
In this section, I present all the relevant code we need to write for the dreaded end-to-end tests.
Since we don't bother with the source code, the starting point is the docker-compose.yml file. The relevant code is:
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:
The file content is pretty straightforward. We can summarize the things defined in the following list:
For further Docker Compose reference, you may have a look here. Now, let's see the end-to-end test code.
The ginkgo testing framework helps us in building the test suite. It's entirely written in Go. Furthermore, it provides a CLI utility to set up and run the tests. Since we will use it later, let's download it from here. You can download it in two ways:
To check whether you have a working utility on your machine, you can run the command ginkgo version (at the time of writing, I have the version 2.20.2).
Please note that the ginkgo command is not mandatory to run the tests. You can still run the tests without this utility by sticking to the go test command.
However, I strongly suggest downloading it since we will use it to generate some boilerplate code.
Located in the root directory, let's create a folder called end2end to host our tests. Within that folder, initialize a Go module by issuing the command go mod init path/to/your/module.
Now, it's time to run the command ginkgo bootstrap. It should generate a new file called end2end_suite_test.go. This file triggers the test suite we'll define in a bit.
This approach is similar to the one with the testify/suite package. It enforces the code modularity and robustness since the definition and running phases are separated.
Now, let's add the tests to our suite. To generate the file where our tests will live, run another ginkgo command: ginkgo generate accounts. This time, the file accounts_test.go pops out. For now, let's leave it as is and switch to the terminal. We fix the missing packages by running the Go command go mod tidy to download the missing dependencies locally on our machine.
Let's start with the entry point of the test suite. The content of the file looks neat:
//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") }
The only unusual thing might be the dot-import within the import section. You can read more about it in the documentation here.
At some points, we need some magic to get to the next testing level. It happened to be testcontainers-go. For the sake of this demo, we use the compose module (for further reference, please refer to here).
This tool can run the compose file we saw earlier and execute the end-to-end tests against the running containers.
This is an extract of the testcontainers-go capabilities. If you want to learn more, please refer to the doc or reach out. I'll be happy to walk you through its stunning features.
This package allows running the end-to-end suite with a single command. It's a more consistent and atomic way to run these tests. It allows me to:
Having the code written this way can help you avoid the hassle of dealing with docker cli commands and makefiles.
Now, let's look at the code where our tests live.
//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)) }) }) }) })
At first glimpse, it might seem hard to digest. To keep things easier, let's break it down into smaller parts.
The Describe container node is nothing but a wrapper to hold the relevant code for our suite. Everything must live within it. It's part of the scaffolded code: var _ = Describe("accounts", Ordered, func() {}. Within the {}, you should put all of the relevant code. To enforce the usage of setup nodes (like BeforeAll), we must define the Describe container as Ordered.
Go 컴파일러가 불평을 하기 때문에 추가하는 것을 잊어버렸다고 걱정하지 마세요.
다음으로 넘어가겠습니다.
이 노드를 사용하면 공통 설정 로직을 추출할 수 있습니다. 이 코드 부분은 제품군 내의 테스트 한 번 및 전에 실행됩니다. 현재 진행 상황을 요약해 보겠습니다.
이 블로그 게시물을 더 길게 만들고 싶지 않아서 테스트 코드를 단순화했습니다. ?.
테스트 기능은 컨테이너 노드 설명 내에 있습니다. Ginkgo가 테스트 사양을 어떻게 처리하는지 여기를 참고하시면 알 수 있습니다. 설명 노드를 사용하면 범위에 따라 테스트를 그룹화하고 구성할 수 있습니다. 이 노드를 다른 설명 노드 안에 중첩할 수 있습니다.
Describe 노드를 더 많이 중첩할수록 테스트 범위가 더 좁아집니다.
그런 다음 상위 설명을 한정하는 컨텍스트 컨테이너 노드가 있습니다. 이는 테스트가 유효한 상황을 규정합니다. 마지막으로 Spec 주제인 It 섹션이 있습니다. 이는 우리가 수행하는 실제 테스트이며 계층 구조 트리의 리프 수준입니다. 테스트 코드는 설명이 필요하므로 테스트를 실행하는 섹션으로 이동하겠습니다.
축하해요? 우리는 여기까지 왔습니다. 이제 우리는 테스트 실행 작업만을 놓치고 있습니다. 눈 깜짝할 사이에 테스트 실행 보고서가 터미널에 인쇄됩니다.
터미널로 전환하고 ginkgo --tags=integration -v 명령을 실행해 보겠습니다. 잠시 후 터미널에 출력이 인쇄된 것을 볼 수 있습니다.
이 블로그 게시물에 많은 내용이 요약되어 있다는 것을 알고 있습니다. 내 목표는 좋은 테스트 도구 모음을 작성하는 방법에 대한 통찰력과 접근 방식을 제공하는 것이었습니다. 제시된 도구, 패키지 및 기술을 다른 종류의 테스트 또는 사용 사례에 맞게 조정할 수 있습니다.
떠나기 전에 testcontainers-go 패키지 구성 모듈의 또 다른 장점을 강조하고 싶습니다.
제가 제공한 구성을 고수하면 최신 Docker 이미지를 사용하게 되며 오래된 이미지 사용으로 인한 문제 해결에 몇 시간씩 소요되는 일을 피할 수 있습니다. 이는 docker compose build --no-cache && docker compose up 명령과 유사합니다. 나한테 고마워할 거야?
관심 가져주셔서 감사합니다 여러분! 질문, 의심, 피드백 또는 의견이 있으시면 함께 듣고 이야기할 수 있습니다. 특정 개념을 다루기를 원하시면 저에게 연락해 주세요. 다음 시간까지 몸조심하시고 만나요 ?
위 내용은 testcontainers-go 및 docker-compose로 테스트 스위트 활용의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!