> 백엔드 개발 > Golang > testcontainers-go 및 docker-compose로 테스트 스위트 활용

testcontainers-go 및 docker-compose로 테스트 스위트 활용

Linda Hamilton
풀어 주다: 2024-10-04 16:07:02
원래의
240명이 탐색했습니다.

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

Now, you should have a clearer idea of our goal. So, let's jump into the test code.

Let's Have Fun ?

In this section, I present all the relevant code we need to write for the dreaded end-to-end tests.

The docker-compose.yml file

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:

  • The mysqldb service doesn't deserve any further explanations
  • The api_service service is the system we're testing
  • The springapimysql-net network hosts the two services defined above

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

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:

  1. By using the go install command (if you've installed Go on your system)
  2. By downloading the compiled binary (useful if you don't have Go installed on your system)

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.

Lay the Foundation with Ginkgo

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.

The end2end_suite_test.go file

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.

Whoop! A wild testcontainers appears ?

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:

  1. Start the containers before the suite
  2. Run the tests relying on these containers
  3. Teardown of containers after the suite and cleanup of the resources used

Having the code written this way can help you avoid the hassle of dealing with docker cli commands and makefiles.

The accounts_test.go file

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

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 컴파일러가 불평을 하기 때문에 추가하는 것을 잊어버렸다고 걱정하지 마세요.

다음으로 넘어가겠습니다.

BeforeAll 설정 노드

이 노드를 사용하면 공통 설정 로직을 추출할 수 있습니다. 이 코드 부분은 제품군 내의 테스트 한 번전에 실행됩니다. 현재 진행 상황을 요약해 보겠습니다.

  • 환경 변수 TESTCONTAINERS_RYUK_DISABLED를 true로 설정합니다. 여기에서 구성에 대해 알아볼 수 있습니다. 류크에 대해 궁금하신 분들은 이걸 보시면 될 것 같아요
  • 제공된 docker-compose.yml 파일을 기반으로 *tc.DockerCompose 변수를 생성합니다
  • 함수 호출을 연기하여 컨테이너 종료 및 리소스 정리
  • compose 스택을 시작하고 api_service라는 컨테이너가 작동되어 8080/tcp 포트에서 수신 대기할 준비가 될 때까지 기다립니다

이 블로그 게시물을 더 길게 만들고 싶지 않아서 테스트 코드를 단순화했습니다. ?.

마지막으로 테스트입니다! ?

테스트 기능은 컨테이너 노드 설명 내에 있습니다. Ginkgo가 테스트 사양을 어떻게 처리하는지 여기를 참고하시면 알 수 있습니다. 설명 노드를 사용하면 범위에 따라 테스트를 그룹화하고 구성할 수 있습니다. 이 노드를 다른 설명 노드 안에 중첩할 수 있습니다.

Describe 노드를 더 많이 중첩할수록 테스트 범위가 더 좁아집니다.

그런 다음 상위 설명을 한정하는 컨텍스트 컨테이너 노드가 있습니다. 이는 테스트가 유효한 상황을 규정합니다. 마지막으로 Spec 주제인 It 섹션이 있습니다. 이는 우리가 수행하는 실제 테스트이며 계층 구조 트리의 리프 수준입니다. 테스트 코드는 설명이 필요하므로 테스트를 실행하는 섹션으로 이동하겠습니다.

3, 2, 1...?

축하해요? 우리는 여기까지 왔습니다. 이제 우리는 테스트 실행 작업만을 놓치고 있습니다. 눈 깜짝할 사이에 테스트 실행 보고서가 터미널에 인쇄됩니다.

터미널로 전환하고 ginkgo --tags=integration -v 명령을 실행해 보겠습니다. 잠시 후 터미널에 출력이 인쇄된 것을 볼 수 있습니다.

마무리 메모 ?

이 블로그 게시물에 많은 내용이 요약되어 있다는 것을 알고 있습니다. 내 목표는 좋은 테스트 도구 모음을 작성하는 방법에 대한 통찰력과 접근 방식을 제공하는 것이었습니다. 제시된 도구, 패키지 및 기술을 다른 종류의 테스트 또는 사용 사례에 맞게 조정할 수 있습니다.

떠나기 전에 testcontainers-go 패키지 구성 모듈의 또 다른 장점을 강조하고 싶습니다.

제가 제공한 구성을 고수하면 최신 Docker 이미지를 사용하게 되며 오래된 이미지 사용으로 인한 문제 해결에 몇 시간씩 소요되는 일을 피할 수 있습니다. 이는 docker compose build --no-cache && docker compose up 명령과 유사합니다. 나한테 고마워할 거야?

관심 가져주셔서 감사합니다 여러분! 질문, 의심, 피드백 또는 의견이 있으시면 함께 듣고 이야기할 수 있습니다. 특정 개념을 다루기를 원하시면 저에게 연락해 주세요. 다음 시간까지 몸조심하시고 만나요 ?

위 내용은 testcontainers-go 및 docker-compose로 테스트 스위트 활용의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:dev.to
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
저자별 최신 기사
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿