Dockertest 极速搭建集成测试环境神器

GoCN

共 7630字,需浏览 16分钟

 ·

2022-05-15 14:12

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 dbname=postgres password=123456 sslmode=disable")
  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-

3 总结

使用Dockertest可以在不修改业务逻辑或者服务模式的情况下就能够对应用服务组件进行集成测试,上手无需任何额外技能,很易用,推荐大家使用。
欢迎加入 GOLANG 中国社区:
 https://gocn.vip

参考资料

  • https://github.com/ory/dockertest

  • https://pkg.go.dev/github.com/ory/dockertest#section-readme

  • https://jonnylangefeld.com/blog/how-to-write-a-go-api-part-3-testing-with-dockertest

  • https://medium.com/easyread/integration-test-database-in-golang-using-dockertest-59ed3b35240e


想要了解更多相关的内容,欢迎扫描下方👇 关注 公众号,回复关键词 [实战群]  ,就有机会进群和我们进行交流~

浏览 11
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报