如何在GoFiber中执行单元测试:Mock还是临时数据库?
Go单元测试:选Mock还是临时数据库?
针对你的场景,两种方案各有适用场景,结合你的代码给你拆解清楚:
一、用Mock的情况(优先推荐新手快速上手)
你的CheckTwoFactor核心逻辑是:解析请求里的id,调用FindUserByID获取用户,根据Two_factor字段返回结果。如果只是想验证业务逻辑分支(比如id解析是否正确、不同Two_factor值的返回是否符合预期),Mock完全够用,而且不需要依赖真实数据库,跑测试速度快。
具体做法:
- 先把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, }) } }
- 测试时用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连接池是否正常工作),就需要用临时数据库。这种属于集成测试,能覆盖更贴近生产环境的场景,但需要额外的环境准备。
具体做法:
- 测试前启动临时数据库:可以用Docker启动临时PostgreSQL容器,或者使用专门的测试数据库实例。
- 测试流程:
- 初始化连接池,创建测试用的用户表
- 插入测试数据
- 调用
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
相关产品推荐
相关产品推荐

