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

如何在GoFiber中执行单元测试:Mock还是临时数据库?

Go单元测试:选Mock还是临时数据库?

针对你的场景,两种方案各有适用场景,结合你的代码给你拆解清楚:

一、用Mock的情况(优先推荐新手快速上手)

你的CheckTwoFactor核心逻辑是:解析请求里的id,调用FindUserByID获取用户,根据Two_factor字段返回结果。如果只是想验证业务逻辑分支(比如id解析是否正确、不同Two_factor值的返回是否符合预期),Mock完全够用,而且不需要依赖真实数据库,跑测试速度快。

具体做法:

  1. 先把DB操作抽象成接口,避免直接依赖pgxpool.Pool:
// 定义用户查询接口
type UserRepository interface {
    FindUserByID(ctx context.Context, userID int64) (User, error)
}

// 基于真实DB的实现
type DBUserRepo struct {
    pool *pgxpool.Pool
}

func (r *DBUserRepo) FindUserByID(ctx context.Context, userID int64) (User, error) {
    // 这里放原来的FindUserByID查询逻辑,用r.pool操作数据库
}

// 修改CheckTwoFactor,依赖接口而非直接依赖pgxpool
func CheckTwoFactor(repo UserRepository) fiber.Handler {
    return func(ctx *fiber.Ctx) error {
        userId := ctx.FormValue("id")
        userID, err := strconv.Atoi(userId)
        if err != nil { // 补上原来忽略的错误处理,测试时可覆盖该分支
            return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid id"})
        }

        User, err := repo.FindUserByID(ctx.Context(), int64(userID))
        if err != nil {
            log.Printf("Error when finding user by ID: %v", err)
            return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "user not found"})
        }
        return ctx.JSON(fiber.Map{
            "result": User.Two_factor,
        })
    }
}
  1. 测试时用Mock实现接口(借助testify/mock库简化操作):
import "github.com/stretchr/testify/mock"

type MockUserRepo struct {
    mock.Mock
}

func (m *MockUserRepo) FindUserByID(ctx context.Context, userID int64) (User, error) {
    args := m.Called(ctx, userID)
    return args.Get(0).(User), args.Error(1)
}

// 测试用例
func TestCheckTwoFactor(t *testing.T) {
    mockRepo := new(MockUserRepo)
    handler := CheckTwoFactor(mockRepo)

    // 场景1:用户开启双因素验证
    mockRepo.On("FindUserByID", mock.Anything, int64(1)).Return(User{Two_factor: true}, nil)
    req := httptest.NewRequest("POST", "/check-two-factor", strings.NewReader("id=1"))
    req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
    resp, err := handler(fiber.AcquireCtx(req))
    assert.NoError(t, err)
    assert.Equal(t, fiber.StatusOK, resp.StatusCode())
    var result map[string]bool
    json.Unmarshal(resp.Body(), &result)
    assert.True(t, result["result"])

    // 场景2:用户未开启双因素验证
    mockRepo.On("FindUserByID", mock.Anything, int64(2)).Return(User{Two_factor: false}, nil)
    req2 := httptest.NewRequest("POST", "/check-two-factor", strings.NewReader("id=2"))
    req2.Header.Set("Content-Type", "application/x-www-form-urlencoded")
    resp2, err := handler(fiber.AcquireCtx(req2))
    assert.NoError(t, err)
    assert.Equal(t, fiber.StatusOK, resp2.StatusCode())
    json.Unmarshal(resp2.Body(), &result)
    assert.False(t, result["result"])

    // 场景3:用户不存在
    mockRepo.On("FindUserByID", mock.Anything, int64(999)).Return(User{}, errors.New("user not found"))
    req3 := httptest.NewRequest("POST", "/check-two-factor", strings.NewReader("id=999"))
    req3.Header.Set("Content-Type", "application/x-www-form-urlencoded")
    resp3, err := handler(fiber.AcquireCtx(req3))
    assert.NoError(t, err)
    assert.Equal(t, fiber.StatusInternalServerError, resp3.StatusCode())
}

二、用临时数据库的情况

如果要验证真实的数据库交互逻辑(比如FindUserByID里的SQL语句是否正确、DB连接池是否正常工作),就需要用临时数据库。这种属于集成测试,能覆盖更贴近生产环境的场景,但需要额外的环境准备。

具体做法:

  1. 测试前启动临时数据库:可以用Docker启动临时PostgreSQL容器,或者使用专门的测试数据库实例。
  2. 测试流程:
    • 初始化连接池,创建测试用的用户表
    • 插入测试数据
    • 调用CheckTwoFactor传入真实连接池
    • 验证返回结果
    • 测试结束后清理数据、销毁连接池

示例代码(简化版):

func TestCheckTwoFactorWithRealDB(t *testing.T) {
    // 1. 连接测试用DB(提前准备好测试实例,或用Docker API在测试中动态启动)
    pool, err := pgxpool.New(context.Background(), "postgres://user:pass@localhost:5432/test_db")
    assert.NoError(t, err)
    defer pool.Close()

    // 2. 创建测试表
    _, err = pool.Exec(context.Background(), `CREATE TABLE users (id SERIAL PRIMARY KEY, two_factor BOOLEAN)`)
    assert.NoError(t, err)
    defer pool.Exec(context.Background(), `DROP TABLE users`)

    // 3. 插入测试数据
    _, err = pool.Exec(context.Background(), `INSERT INTO users (id, two_factor) VALUES (1, true), (2, false)`)
    assert.NoError(t, err)

    // 4. 测试Handler
    repo := &DBUserRepo{pool: pool}
    handler := CheckTwoFactor(repo)

    req := httptest.NewRequest("POST", "/check-two-factor", strings.NewReader("id=1"))
    req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
    resp, err := handler(fiber.AcquireCtx(req))
    assert.NoError(t, err)
    assert.Equal(t, fiber.StatusOK, resp.StatusCode())
    var result map[string]bool
    json.Unmarshal(resp.Body(), &result)
    assert.True(t, result["result"])
}

总结

  • 如果你只是想快速验证CheckTwoFactor里的业务逻辑(参数解析、返回分支),Mock是最优选择,不需要额外环境,测试速度快,适合单元测试。
  • 如果你要验证DB查询的正确性、整个流程和真实DB的交互,临时数据库更合适,属于集成测试,能发现DB层面的问题,但需要准备测试环境。

作为新手,建议先从Mock入手,把业务逻辑的测试覆盖全,之后再逐步补充集成测试。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 17:30:38