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

如何使用Mockgen为Go项目的Sqlx数据库实现Mock测试

Sqlx 搭配 Mockgen 实现数据库层Mock实操

sqlx没有自动生成接口的能力,手动做一层接口抽象就能适配Mockgen完成数据库Mock,具体步骤如下:

  • 第一步:抽离数据库操作接口
    你当前代码里handler直接依赖*sqlx.DB具体实现,本身就不支持Mock,先把所有业务用到的数据库操作统一抽象成接口。建议在internal/model下新建store.go,定义Store接口,把你internal/queries/queries.go里所有数据库操作方法的签名都列进去,同时写一个基于sqlx的真实实现结构体,封装*sqlx.DB实现这个接口。
    示例代码:

    // internal/model/store.go
    package model
    
    import (
      "context"
      "github.com/jmoiron/sqlx"
    )
    
    // Store 定义所有业务需要的数据库操作
    type Store interface {
      GetUser(ctx context.Context, id int64) (*User, error)
      GetAllUser(ctx context.Context) ([]*User, error)
      CreateUser(ctx context.Context, u *User) error
      GetUserAddress(ctx context.Context, userID int64) ([]*Address, error)
      CreateUserAddress(ctx context.Context, userID int64, addr *Address) error
      UpdateUser(ctx context.Context, u *User) error
      UpdateUserAddress(ctx context.Context, addrID int64, addr *Address) error
      DisableUser(ctx context.Context, id int64) error
      EnableUser(ctx context.Context, id int64) error
      DeleteUser(ctx context.Context, id int64) error
      DeleteUserAddress(ctx context.Context, addrID int64) error
    }
    
    // SqlxStore 是Store接口的真实数据库实现
    type SqlxStore struct {
      db *sqlx.DB
    }
    
    // 实现Store接口的所有方法,把你原来queries.go里的查询逻辑直接挪过来就行
    func (s *SqlxStore) GetUser(ctx context.Context, id int64) (*User, error) {
      var u User
      err := s.db.GetContext(ctx, &u, "SELECT * FROM users WHERE id = $1", id)
      return &u, err
    }
    
    // 其余方法按同样逻辑实现,把原来函数里的db.xxx调用换成s.db.xxx即可
    
    func NewSqlxStore(db *sqlx.DB) Store {
      return &SqlxStore{db: db}
    }
    

    如果觉得手动写接口签名麻烦,用接口生成工具扫queries.go里的函数直接生成即可,不用逐个敲。

  • 第二步:替换现有代码的依赖类型
    把项目里所有直接依赖*sqlx.DB的地方,全换成model.Store接口类型:

    1. 改internal/api/app.go里的App结构体:
    type App struct {
      Router *httprouter.Router
      DB     model.Store // 原来的*sqlx.DB替换成接口
    }
    
    1. 改App初始化逻辑,原来的a.DB = model.DBMigrate(db)替换成a.DB = model.NewSqlxStore(model.DBMigrate(db))
    2. 所有handler函数的入参,原来第一个参数是*sqlx.DB的,全换成model.Store,业务逻辑不用做任何修改,因为接口方法和你之前写的查询函数签名完全一致。
  • 第三步:用Mockgen生成Mock实现
    先安装mockgen工具,执行命令:

    go install go.uber.org/mock/mockgen@latest
    

    然后在项目根目录执行生成命令,自动生成mock代码:

    mockgen -source=internal/model/store.go -destination=internal/mocks/mock_store.go -package=mocks
    

    生成的mock代码里已经自带了期望调用设置、返回值自定义的能力,不用手写任何mock逻辑。

  • 第四步:单元测试中使用Mock
    写测试的时候直接初始化MockStore,预设对应方法的返回值,传给handler即可,不会触发真实数据库连接。示例:

    // internal/handler/users_test.go
    package handler
    
    import (
      "GO_APP/internal/mocks"
      "GO_APP/internal/model"
      "net/http"
      "net/http/httptest"
      "testing"
      "github.com/julienschmidt/httprouter"
      "go.uber.org/mock/gomock"
      "github.com/stretchr/testify/assert"
    )
    
    func TestGetUser(t *testing.T) {
      ctrl := gomock.NewController(t)
      defer ctrl.Finish()
    
      // 初始化mock数据库实例
      mockStore := mocks.NewMockStore(ctrl)
      // 预设调用行为:传入任意context、用户id=1时,返回测试用的用户数据
      testUser := &model.User{ID: 1, Name: "test", Email: "test@example.com"}
      mockStore.EXPECT().GetUser(gomock.Any(), int64(1)).Return(testUser, nil)
    
      // 构造测试请求
      req := httptest.NewRequest(http.MethodGet, "/users/1", nil)
      w := httptest.NewRecorder()
      ps := httprouter.Params{{Key: "id", Value: "1"}}
    
      // 传入mockStore调用handler,无真实DB交互
      GetUser(mockStore, w, req, ps)
    
      // 校验返回结果
      assert.Equal(t, http.StatusOK, w.Code)
    }
    

额外说明:手动抽Store接口不是额外工作量,这种分层方式本身就会降低代码耦合度,后续要换数据库驱动、换ORM,只要实现Store接口就行,上层handler、api层的代码完全不用改,比直接依赖具体的*sqlx.DB结构更易维护。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 03:18:27