You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言Docker环境下PostgreSQL集成测试方案咨询

标准Go数据库交互测试方案

针对你的场景,核心思路是解耦数据库依赖 + 用dockertest创建隔离的测试数据库环境,既满足Get函数对指定数据库的依赖,又保证测试的独立性和可重复性。

一、重构代码解耦数据库依赖

当前Get函数直接依赖全局的database.DB,这会导致测试时无法替换为测试用数据库。先做简单重构:

修改 customers.go

import (
    "time"
    "gorm.io/gorm"
)

type Customers struct {
    ID           int       
    CustomerName string   
    Active       bool      
    Balance      float32   
    ActiveSince  time.Time
}

// 让Get函数接受*gorm.DB参数,不再依赖全局变量
func Get(db *gorm.DB, id int) (Customers, error) {
    var customer Customers
    result := db.First(&customer, id)

    if result.Error != nil {
        return Customers{}, result.Error
    }
    return customer, nil
}

(可选)优化 database.go 的灵活性

如果需要在测试中复用连接逻辑,可以新增一个带参数的连接函数:

import (
    "fmt"
    "gorm.io/driver/postgres"
    "gorm.io/gorm"
    "os"
)

var DB *gorm.DB
var err error

func Open() error {
    dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=disable", 
        os.Getenv("HOST"), os.Getenv("USER"), os.Getenv("PASSWORD"), os.Getenv("DB"), os.Getenv("PORT"))
    return OpenWithDSN(dsn)
}

// 新增:通过指定DSN连接数据库,方便测试时传入测试用DSN
func OpenWithDSN(dsn string) error {
    DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
    return err
}

// 新增:获取DB实例的方法,也可以直接用全局变量,但推荐封装
func GetDB() *gorm.DB {
    return DB
}

二、用dockertest搭建测试数据库

dockertest启动新PostgreSQL容器后,你可以在容器内创建指定名称的数据库,再迁移表结构、插入测试数据,完全模拟生产环境的数据库状态。

编写测试文件 customers_test.go

import (
    "fmt"
    "testing"
    "time"

    "github.com/ory/dockertest/v3"
    "github.com/ory/dockertest/v3/docker"
    "gorm.io/driver/postgres"
    "gorm.io/gorm"
)

func TestGetCustomer(t *testing.T) {
    // 1. 启动PostgreSQL Docker容器
    pool, err := dockertest.NewPool("")
    if err != nil {
        t.Fatalf("无法创建dockertest池: %v", err)
    }

    resource, err := pool.RunWithOptions(&dockertest.RunOptions{
        Repository: "postgres",
        Tag:        "14",
        Env: []string{
            "POSTGRES_PASSWORD=testpass",
            "POSTGRES_USER=testuser",
            // 先连接默认postgres库,后续创建业务指定的数据库
            "POSTGRES_DB=postgres",
        },
    }, func(config *docker.HostConfig) {
        config.AutoRemove = true
        config.RestartPolicy = docker.RestartPolicy{Name: "no"}
    })
    if err != nil {
        t.Fatalf("无法启动PostgreSQL容器: %v", err)
    }

    // 确保容器停止后清理资源
    t.Cleanup(func() {
        if err := pool.Purge(resource); err != nil {
            t.Logf("无法清理容器: %v", err)
        }
    })

    // 2. 等待数据库就绪,创建业务指定的数据库
    resource.Expire(120) // 2分钟后自动清理容器
    var testDB *gorm.DB
    err = pool.Retry(func() error {
        // 连接默认postgres库
        dsn := fmt.Sprintf("host=localhost user=testuser password=testpass dbname=postgres port=%s sslmode=disable", resource.GetPort("5432/tcp"))
        db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
        if err != nil {
            return err
        }

        // 创建你业务中指定的数据库(替换成你的os.Getenv("DB")值,比如"customers_db")
        if err := db.Exec("CREATE DATABASE customers_db").Error; err != nil {
            return err
        }

        // 连接到刚创建的业务数据库
        testDSN := fmt.Sprintf("host=localhost user=testuser password=testpass dbname=customers_db port=%s sslmode=disable", resource.GetPort("5432/tcp"))
        testDB, err = gorm.Open(postgres.Open(testDSN), &gorm.Config{})
        return err
    })
    if err != nil {
        t.Fatalf("无法连接到测试数据库: %v", err)
    }

    // 3. 迁移表结构(和生产环境一致)
    if err := testDB.AutoMigrate(&Customers{}); err != nil {
        t.Fatalf("迁移表结构失败: %v", err)
    }

    // 4. 插入测试数据
    testCustomer := Customers{
        CustomerName: "测试用户",
        Active:       true,
        Balance:      100.5,
        ActiveSince:  time.Now(),
    }
    if err := testDB.Create(&testCustomer).Error; err != nil {
        t.Fatalf("插入测试数据失败: %v", err)
    }

    // 5. 测试Get函数
    result, err := Get(testDB, int(testCustomer.ID))
    if err != nil {
        t.Fatalf("调用Get函数失败: %v", err)
    }

    // 断言结果
    if result.CustomerName != testCustomer.CustomerName {
        t.Errorf("期望用户名: %s, 实际得到: %s", testCustomer.CustomerName, result.CustomerName)
    }
    if result.Balance != testCustomer.Balance {
        t.Errorf("期望余额: %.2f, 实际得到: %.2f", testCustomer.Balance, result.Balance)
    }
}

三、测试运行说明

  1. 确保本地Docker处于运行状态
  2. 安装dockertest依赖:go get github.com/ory/dockertest/v3
  3. 运行测试:go test ./... -v

这种方案的优势:

  • 完全隔离测试环境,不会污染生产或开发数据库
  • 模拟真实的数据库交互,测试结果可靠
  • 解耦后的代码更易维护,符合Go的依赖注入思想

内容的提问来源于stack exchange,提问作者mevoker659

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 23:50:23