golang轻量级零依赖Docker测试容器插件库dft的使用
Golang轻量级零依赖Docker测试容器插件库dft的使用
🥼 DFT简介
DFT(Docker For Testing)是一个零依赖的docker
命令包装器,完全基于标准库实现。
唯一要求:运行中的docker守护进程。
该包旨在用于各种测试设置,从本地测试到CI/CD流水线。其主要目标是减少对模拟对象(特别是数据库模拟)的需求,并降低测试所需的包数量。
容器可以通过端口、环境变量或[CMD]覆盖等选项启动。
👓 示例代码
下面是一个测试使用MongoDB支持的用户服务的完整示例:
package myawesomepkg_test
import (
"context"
"testing"
"time"
"my/awesome/pkg/repository"
"my/awesome/pkg/user"
"github.com/abecodes/dft"
)
func TestUserService(tt *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// 启动一个MongoDB容器
ctr, err := dft.StartContainer(
ctx,
"mongo:7-jammy",
dft.WithRandomPort(27017),
)
if err != nil {
tt.Errorf("[dft.StartContainer] unexpected error: %v", err)
tt.FailNow()
return
}
// 等待数据库准备就绪
err = ctr.WaitCmd(
ctx,
[]string{
"mongosh",
"--norc",
"--quiet",
"--host=localhost:27017",
"--eval",
"'db.getMongo()'",
},
func(stdOut string, stdErr string, code int) bool {
tt.Logf("got:\n\tcode:%d\n\tout:%s\n\terr:%s\n", code, stdOut, stdErr)
return code == 0
},
// 由于我们使用随机端口,需要在容器内执行命令
dft.WithExecuteInsideContainer(true),
)
if err != nil {
tt.Errorf("[dft.WaitCmd] wait error: %v", err)
tt.FailNow()
return
}
// 确保测试完成后清理容器
defer func() {
if ctr != nil {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
ctr.Stop(ctx)
cancel()
}
}()
// 获取容器暴露的端口地址
addrs, ok := ctr.ExposedPortAddresses(27017)
if !ok {
tt.Error("[ctr.ExposedPortAddresses] did not return any addresses")
tt.FailNow()
return
}
// 获取数据库连接
conn := createNewMongoClient(addrs[0])
// 创建用户仓库
userRepo := repository.User(conn)
// 启动用户服务
userSvc := user.New(userRepo)
defer userSvc.Close()
tt.Run(
"it can store a new user in the database",
func(t *testing.T) {
// 创建新用户
userSvc.New("awesome", "user")
// 验证写入
users := userRepo.GetAll()
if len(users) != 1 &&
users[0].FirstName != "awesome" &&
users[0].LastName != "user" {
t.Error("[userSvc.New] unable to create user")
tt.FailNow()
return
}
},
)
}
🤖 API参考
StartContainer选项
选项 | 说明 | 示例 |
---|---|---|
WithCmd | 覆盖[CMD] | WithCmd([]string{"--tlsCAFile", "/run/tls/ca.crt"}) |
WithEnvVar | 设置容器内环境变量,可多次调用,相同key会覆盖 | WithEnvVar("intent", "prod") |
WithMount | 挂载本地目录或文件,可多次调用 | WithMount("./host/folder", "/target") |
WithPort | 将内部端口映射到特定主机端口 | WithPort(27017,8080) |
WithRandomPort | 将内部端口映射到随机主机端口,使用ExposedPorts 或ExposedPortAddresses 获取实际端口 |
WithRandomPort(27017) |
Wait选项
选项 | 说明 | 示例 |
---|---|---|
WithExecuteInsideContainer | 是否在容器内执行命令(默认false),适用于容器特有命令 | WithExecuteInsideContainer(true) |
这个库非常适合在Golang测试中快速启动和操作Docker容器,无需复杂的依赖关系,使测试更加真实可靠。
更多关于golang轻量级零依赖Docker测试容器插件库dft的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang轻量级零依赖Docker测试容器插件库dft的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang轻量级零依赖Docker测试容器插件库dft使用指南
dft (Docker For Test) 是一个轻量级、零依赖的Golang库,专门用于在单元测试和集成测试中管理Docker容器。它提供了简单的API来启动、停止和管理测试所需的容器服务。
主要特性
- 零外部依赖(仅依赖标准库和Docker引擎API)
- 轻量级实现
- 支持容器生命周期管理
- 自动清理测试容器
- 支持自定义容器配置
安装
go get github.com/yourusername/dft
基本使用示例
1. 启动一个Redis测试容器
package main
import (
"context"
"fmt"
"testing"
"time"
"github.com/yourusername/dft"
)
func TestWithRedis(t *testing.T) {
// 创建dft客户端
client, err := dft.NewClient()
if err != nil {
t.Fatalf("Failed to create client: %v", err)
}
defer client.Close()
// 定义Redis容器配置
redisCfg := &dft.ContainerConfig{
Image: "redis:alpine",
ExposedPorts: []string{"6379/tcp"},
WaitingPorts: []string{"6379"},
WaitingPeriod: 5 * time.Second,
}
// 启动Redis容器
redisContainer, err := client.RunContainer(context.Background(), redisCfg)
if err != nil {
t.Fatalf("Failed to start Redis container: %v", err)
}
defer redisContainer.Stop(context.Background())
// 获取容器映射的端口
port := redisContainer.GetMappedPort("6379")
redisAddr := fmt.Sprintf("localhost:%s", port)
// 这里可以使用redisAddr连接测试Redis
t.Logf("Redis is running at %s", redisAddr)
// 测试逻辑...
}
2. 启动PostgreSQL测试容器
func TestWithPostgreSQL(t *testing.T) {
client, err := dft.NewClient()
if err != nil {
t.Fatal(err)
}
defer client.Close()
pgCfg := &dft.ContainerConfig{
Image: "postgres:13-alpine",
ExposedPorts: []string{"5432/tcp"},
Env: []string{"POSTGRES_PASSWORD=testpass"},
WaitingPorts: []string{"5432"},
WaitingPeriod: 10 * time.Second,
}
pgContainer, err := client.RunContainer(context.Background(), pgCfg)
if err != nil {
t.Fatal(err)
}
defer pgContainer.Stop(context.Background())
port := pgContainer.GetMappedPort("5432")
dsn := fmt.Sprintf("postgres://postgres:testpass@localhost:%s/postgres?sslmode=disable", port)
t.Logf("PostgreSQL DSN: %s", dsn)
// 测试数据库连接和逻辑...
}
高级功能
自定义容器配置
cfg := &dft.ContainerConfig{
Image: "my-custom-image",
ExposedPorts: []string{"8080/tcp", "9090/tcp"},
Env: []string{"ENV_VAR=value", "ANOTHER_VAR=123"},
Cmd: []string{"myapp", "--debug"},
// 等待特定日志输出才认为容器就绪
WaitingFor: &dft.WaitForLog{
Pattern: "Server started",
Timeout: 30 * time.Second,
},
}
多容器测试
func TestMultiContainer(t *testing.T) {
client, err := dft.NewClient()
if err != nil {
t.Fatal(err)
}
defer client.Close()
// 启动Redis
redisCfg := &dft.ContainerConfig{
Image: "redis:alpine",
ExposedPorts: []string{"6379/tcp"},
}
redis, err := client.RunContainer(context.Background(), redisCfg)
if err != nil {
t.Fatal(err)
}
defer redis.Stop(context.Background())
// 启动应用容器,链接到Redis
appCfg := &dft.ContainerConfig{
Image: "myapp:test",
ExposedPorts: []string{"8080/tcp"},
Env: []string{
fmt.Sprintf("REDIS_URL=redis://%s:6379", redis.GetIP()),
},
}
app, err := client.RunContainer(context.Background(), appCfg)
if err != nil {
t.Fatal(err)
}
defer app.Stop(context.Background())
// 测试逻辑...
}
最佳实践
- 每个测试用例独立容器:为每个测试用例启动独立的容器,避免测试间相互影响
- 使用defer清理:确保测试结束后清理容器
- 合理设置等待时间:根据服务启动时间设置适当的WaitingPeriod
- 复用客户端:在测试套件级别创建dft客户端,而不是每个测试用例都创建
与类似库的比较
相比于testcontainers-go等库,dft的优势在于:
- 更轻量,零外部依赖
- 更简单的API设计
- 更适合小型项目或简单测试场景
不足:
- 功能相对较少
- 社区支持较小
dft非常适合需要轻量级解决方案的测试场景,特别是当项目不希望引入过多依赖时。