这个项目能做什么
# go-docker-testsuite
一个用于在 Docker 容器中运行第三方依赖以进行集成测试的 Go 库。它让你可以启动任意 Docker 镜像——数据库、队列、缓存或对象存储——并从 Go 测试中连接它们。
## 特性
- **Container** — 用于创建、运行、等待输出并清理任意 Docker 镜像的底层封装
- **Group** — 在具有 IP 级连通性的隔离 Docker 网络中运行多个容器
- **Applications** — 针对流行服务(MySQL、PostgreSQL、Redis、Kafka 等)的开箱即用封装
- **Hooks** — 每个容器的生命周期回调(BeforeRun、AfterRun、BeforeClose、AfterClose)
- **Exec** — 在运行中的容器内执行命令并捕获 stdout、stderr 和退出码
- **生命周期命令** — 通过 exec 运行启动 / 就绪后命令
- **将文件复制到容器中** — 使用 `WithFiles` 在启动前预置文件
- **从容器中复制文件** — 以 tar 流的形式从运行中的容器读回文件
- **资源限制** — 限制 CPU/内存/pids 以保护主机和 CI
- **匹配器** — 使用子串、精确或正则匹配器等待容器日志
- **等待策略** — 可组合的就绪探针(ForLog、ForHTTPGet、ForCommand、ForTCPConnection)
- **环境构建器** — 用于类型化环境变量的流式 DSL
- **端口绑定** — 支持随机或一对一分配的 DNAT 端口映射
- **网络模式** — 支持 host 或自定义网络
- **IMAGE_PREFIX** — 通过代理/镜像路由镜像
- **`*testing.T` 绑定** — 自动清理和日志记录,并支持安全的 `t.Parallel()`
## 要求
- Go 1.26+
- 运行中的 Docker 守护进程(可通过 `DOCKER_HOST` 与远程主机配合使用)
## 安装
多模块工作区:根目录是核心模块,`applications/<name>` 下的每个应用都是独立的 Go 模块。只拉取你需要的部分:
```sh
go get github.com/teran/go-docker-testsuite
# 特定应用封装:
go get github.com/teran/go-docker-testsuite/applications/redis
go get github.com/teran/go-docker-testsuite/applications/postgres
```
## 应用
开箱即用的封装(每个都返回类型化的客户端接口,并处理启动、健康检查和清理):
- Ceph (RGW),配合 AWS SDK v2
- ClickHouse,配合 clickhouse-go
- Forgejo,配合 SQLite
- FRR 路由套件,配合 vtysh
- K3s,配合 client-go
- Kafka,配合 Sarama
- Memcache,配合 gomemcache
- MinIO(兼容 S3)
- MongoDB,配合 mongo-driver
- MySQL / MariaDB / Percona Server
- NetBox,配合 PostgreSQL + Redis
- Nginx
- OpenSearch,配合 opensearch-go
- Paperless-ngx,配合 PostgreSQL + Valkey
- PostgreSQL,配合 pgx
- Prometheus,配合 prometheus/client_golang
- RabbitMQ(AMQP + 管理 API)
- Redis,配合 go-redis
- ScyllaDB,配合 gocql
- Vault
- Libvirtd (KVM/QEMU)
许多包在 pkg.go.dev 上包含可测试的示例。
## 用法
### 快速开始 — MySQL
```go
package main
import (
"context"
"database/sql"
"time"
_ "github.com/go-sql-driver/mysql"
"github.com/teran/go-docker-testsuite/applications/mysql"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
app, err := mysql.New(ctx, "index.docker.io/library/mysql:8.0.4")
if err != nil {
panic(err)
}
defer app.Close(ctx)
if err := app.CreateDB(ctx, "important_database"); err != nil {
panic(err)
}
db, err := sql.Open("mysql", app.MustDSN("important_database"))
if err != nil {
panic(err)
}
defer db.Close()
if _, err := db.ExecContext(ctx, "SELECT 1"); err != nil {
panic(err)
}
}
```
### 多容器组
```go
package main
import (
"context"
"time"
"github.com/teran/go-docker-testsuite"
"github.com/teran/go-docker-testsuite/wait"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
app := docker.NewApplication(
c,
docker.HookFunc(func(ctx context.Context, ht docker.HookType, c docker.Container) error {
return wait.Wait(ctx, c, wait.ForLog(docker.NewSubstringMatcher("ready")))
}),
)
g, err := docker.NewGroup("my-services", app1, app2)
if err != nil {
panic(err)
}
if err := g.Run(ctx); err != nil {
panic(err)
}
defer g.Close(ctx)
}
```
### 等待策略
```go
if err := wait.Wait(ctx, c, wait.ForHTTPGet(8080,
wait.WithPath("/health"),
wait.WithResponseStatuses(200),
)); err != nil {
panic(err)
}
```
其他策略:`ForLog`、`ForCommand`、`ForTCPConnection`。可与 `ForAll` / `ForAny` / `ForAtLeast` 组合使用。
### `*testing.T` 绑定
```go
func TestRedis(t *testing.T) {
t.Parallel()
c, err := docker.NewContainerWithT(
t,
"redis",
images.Redis,
nil,
docker.NewEnvironment(),
docker.NewPortBindings().PortDNAT(docker.ProtoTCP, 6379),
)
if err != nil {
t.Fatal(err)
}
c.RunT(ctx) // 快速失败;注册 t.Cleanup
}
```
### 生命周期钩子
```go
docker.HookTypeBeforeRun // 容器启动前
docker.HookTypeAfterRun // 容器启动后
docker.HookTypeBeforeClose // 容器停止前
docker.HookTypeAfterClose // 容器停止后
```
### Exec 和生命周期命令
```go
res, err := c.Exec(ctx, []string{"echo", "hello"})
if err != nil {
panic(err)
}
if err := res.Error(); err != nil {
panic(err)
}
fmt.Printf("exit code: %d\n", res.ExitCode)
fmt.Printf("stdout: %s", res.Stdout)
```
### 将文件复制到容器中/从容器中复制
对于小内容,使用 `WithFiles` 配合 `FileFromBytes`;对于大文件,使用 `io.Reader` + `Size`。使用 `docker.CopyFromContainer` 读回文件。
### 镜像前缀 / 代理
```sh
export IMAGE_PREFIX=registry-mirror.example.com
```
## 模块与发布
多模块工作区使用单一版本号,但分别打标签(例如核心 `v1.6.0`,应用 `applications/redis/v1.6.0`)。发布顺序:先核心,后应用。使用 Makefile 打标签。
## 许可证
Apache License, Version 2.0
评论
0 评分人数达到10人后显示
登录后参与讨论。