Dockertest 極速搭建集成測試環境神器

1 推薦背景

在開發應用程序時,經常需要與數據庫系統交互的服務,如,各種數據庫,以及 minio,Kafka,Redis 等服務組件,沒錯 Dockertest 基本都支持主流數據庫和服務組件。對這些服務進行集成測試是很麻煩的,因爲模擬數據庫 / 數據庫抽象層是很費勁的一件事。對模式進行細微的更改意味着至少重寫部分 (如果不是全部) 模擬;數據庫抽象層中的 API 變化也是如此。爲了避免這種情況,更聰明的做法是用一個真實的數據庫來測試這些特定的服務,而這個數據庫在測試後就會被銷燬。Docker 是運行集成測試的完美系統,因爲我們可以在幾秒鐘內啓動容器,並在測試完成後殺死它們。

Dockertest 庫提供了易於使用的命令來啓動 Docker 容器並在測試中使用它們,解決以上提到的集成測試問題,不需要通過 Dockerfile 或 docker-compose 手動設置 Docker。

2 怎麼使用

第一步:安裝

go get -u github.com/ory/dockertest/v3

第二步:使用

這裏測試目標是用 Iris 搭建的 Web API ,即用 Dockertest 來啓動 Docker 容器並模擬 Web 服務器和數據庫,請確保環境中已安裝 Docker 。

使用 Dockertest 設置基礎設施的最簡單方法是在測試文件的 TestMain 函數中添加設置代碼,這個測試示例中會生成 Postgres Docker 容器,Web API 的代碼請瀏覽

https://codeload.github.com/jonnylangefeld/go-api-base-project/zip/refs/tags/part-2

下載,並在項目根目錄下創建 main_test.go 文件,寫入以下測試示例代碼

package main

import (
 "encoding/json"
 "fmt"
 "log"
 "os"
 "testing"
 "time"

 "my-go-api/model"

 "github.com/jinzhu/gorm"
 "github.com/kataras/iris"
 "github.com/kataras/iris/httptest"
 "github.com/ory/dockertest"
 "github.com/ory/dockertest/docker"
 "github.com/stretchr/testify/assert"
)

var db *gorm.DB
var app *iris.Application

func TestMain(m *testing.M) {
 // Create a new pool for docker containers
 pool, err := dockertest.NewPool("")
 if err != nil {
  log.Fatalf("Could not connect to docker: %s", err)
 }

 // Pull an image, create a container based on it and set all necessary parameters
 opts := dockertest.RunOptions{
  Repository:   "mdillon/postgis",
  Tag:          "latest",
  Env:          []string{"POSTGRES_PASSWORD=123456"},
  ExposedPorts: []string{"5432"},
  PortBindings: map[docker.Port][]docker.PortBinding{
   "5432": {
    {HostIP: "0.0.0.0", HostPort: "5477"},
   },
  },
 }

 // Run the docker container
 resource, err := pool.RunWithOptions(&opts)
 if err != nil {
  log.Fatalf("Could not start resource: %s", err)
 }

 // Exponential retry to connect to database while it is booting
 if err := pool.Retry(func() error {
  databaseConnStr := fmt.Sprintf("host=localhost port=5477 user=postgres db)
  db, err = gorm.Open("postgres", databaseConnStr)
  if err != nil {
   log.Println("Database not ready yet (it is booting up, wait for a few tries)...")
   return err
  }

  // Tests if database is reachable
  return db.DB().Ping()
 }); err != nil {
  log.Fatalf("Could not connect to docker: %s", err)
 }

 log.Println("Initialize test database...")
 initTestDatabase()

 log.Println("Create new iris app...")
 app = newApp(db)

 // Run the actual test cases (functions that start with Test...)
 code := m.Run()

 // Delete the docker container
 if err := pool.Purge(resource); err != nil {
  log.Fatalf("Could not purge resource: %s", err)
 }

 os.Exit(code)
}

func TestName(t *testing.T) {
 // Request an endpoint of the app
 e := httptest.New(t, app, httptest.URL("http://localhost"))
 t1 := e.GET("/bill").Expect().Status(iris.StatusOK)

 // Compare the actual result with an expected result
 assert.Equal(t, "Hello bill", t1.Body().Raw())
}

func TestOrders(t *testing.T) {
 e := httptest.New(t, app, httptest.URL("http://localhost"))
 t1 := e.GET("/orders").Expect().Status(iris.StatusOK)

 expected, _ := json.Marshal(sampleOrders)
 assert.Equal(t, string(expected), t1.Body().Raw())
}

func initTestDatabase() {
 db.AutoMigrate(&model.Order{})

 db.Save(&sampleOrders[0])
 db.Save(&sampleOrders[1])
}

var sampleOrders = []model.Order{
 {
  ID:          1,
  Description: "An old glove",
  Ts:          time.Now().Unix() * 1000,
 },
 {
  ID:          2,
  Description: "Something you don't need",
  Ts:          time.Now().Unix() * 1000,
 },
}

第三步:運行測試示例

go test -v

執行成功會打印測試結果,失敗會給出反饋:

2022/05/08 10:10:43 Database not ready yet (it is booting up, wait for a few tries)...
2022/05/08 10:10:49 Database not ready yet (it is booting up, wait for a few tries)...
2022/05/08 10:10:55 Initialize test database...
2022/05/08 10:10:55 Create new iris app...
=== RUN   TestName
--- PASS: TestName (0.00s)
=== RUN   TestOrders
--- PASS: TestOrders (0.00s)
PASS
ok      my-go-api       25.706s

以這種方式可以在每個測試模塊上執行,單獨模塊服務在新容器中運行,從而使測試完全獨立,測試完即可銷燬容器,就好像什麼都沒發生一樣。

更多示例請瀏覽:

_https://codeload.github.com/jonnylangefeld/go-api-base-project/zip/refs/tags/part-_2 

3 總結

使用 Dockertest 可以在不修改業務邏輯或者服務模式的情況下就能夠對應用服務組件進行集成測試,上手無需任何額外技能,很易用,推薦大家使用。

歡迎加入 GOLANG 中國社區:

 https://gocn.vip

參考資料

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/hCuBfdlxhx_DxlGBCWjAog