皆さん、おかえりなさい!今日は、興味深いブログ投稿でエンドツーエンドのテストについて取り上げます。このような種類のテストを書いたことがない場合、またはテストを改善しようと努力している場合は、このエキサイティングな旅を説明するので読み続けてください。この記事を最後まで読むと、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 實用程式來設定和運行測試。因為稍後我們會使用它,所以我們從這裡下載它。您可以透過兩種方式下載:
要檢查您的電腦上是否有可用的實用程序,您可以執行命令 ginkgo version (在撰寫本文時,我的版本是 2.20.2)。
請注意,ginkgo 指令不是執行測試所必需的。您仍然可以透過堅持 go test 命令來在沒有此實用程式的情況下執行測試。
但是,我強烈建議下載它,因為我們將使用它來產生一些樣板程式碼。
位於根目錄中,讓我們建立一個名為 end2end 的資料夾來託管我們的測試。在這個資料夾中,透過發出指令 go mod init path/to/your/module 來初始化 Go 模組。
現在,是時候執行 ginkgo bootstrap 指令了。它應該會產生一個名為 end2end_suite_test.go 的新檔案。該文件會觸發我們稍後定義的測試套件。
此方法類似 testify/suite 套件的方法。由於定義和運行階段是分開的,它增強了程式碼的模組化和穩健性。
現在,讓我們將測試添加到我們的套件中。要產生我們的測試所在的文件,請執行另一個 ginkgo 指令:ginkgogeneratecounts。這次,檔案accounts_test.go 彈出。現在,讓我們保持原樣並切換到終端。我們透過執行 Go 命令 go mod tidy 將缺少的依賴項下載到我們的電腦本機來修復遺失的軟體包。
讓我們從測試套件的入口點開始。文件內容看起來很整潔:
//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") }
唯一不尋常的事情可能是導入部分中的點導入。您可以在此處的文檔中閱讀有關它的更多資訊。
在某些時候,我們需要一些魔法才能進入下一個測試等級。恰好是 testcontainers-go。為了這個演示,我們使用 compose 模組(更多參考,請參閱此處)。
該工具可以運行我們之前看到的 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 編譯器會抱怨。
我們繼續吧。
這個節點允許我們提取通用的設定邏輯。此程式碼部分在套件內的測試之前執行一次和。讓我們回顧一下正在做的事情:
我簡化了測試程式碼,因為我不想讓這篇文章變得更長? .
測試函數位於描述容器節點中。您可以參考這裡來了解ginkgo是如何處理測試規範的。描述節點可讓您根據測試範圍對測試進行分組和組織。您可以將此節點巢狀在其他描述節點中。
Describe節點嵌套越多,測試範圍就越窄。
然後,我們就有了限定父描述的上下文容器節點。它限定了測試有效的情況。最後,我們有 It 部分,即 Spec 主題。這是我們正在執行的實際測試,並且是層次結構樹的葉子層級。測試程式碼是不言自明的,因此我將跳到執行測試的部分。
恭喜?我們設法到達這裡。現在,我們只錯過了試運行操作。眨眼間,我們的測試執行報告就會印到終端上。
讓我們切換到終端並執行命令 ginkgo --tags=integration -v。一段時間後,您將看到終端上列印的輸出。
我知道這篇文章濃縮了很多內容。我的目標是提供有關如何編寫良好的測試套件的見解和方法。您可能希望使所提供的工具、套件和技術適應其他類型的測試或用例。
在離開之前,我想強調一下 testcontainers-go 套件的 compose 模組的另一個優點。
如果您堅持使用我提供的配置,您一定會使用最新的 Docker 映像,並且可以避免因使用過時的映像而花費數小時進行故障排除。它類似於命令 docker compose build --no-cache && docker compose up。你會感謝我嗎?
感謝各位朋友的關注!如果您有任何問題、疑慮、回饋或意見,我可以一起傾聽和說話。如果您希望我介紹一些具體概念,請與我聯絡。下次見,保重,再見?
以上是透過 testcontainers-go 和 docker-compose 來利用您的測試套件的詳細內容。更多資訊請關注PHP中文網其他相關文章!