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

如何用Gomock测试闭包依赖外部包结构体的Go方法?

问题:Gomock测试闭包依赖model结构体的业务方法

项目代码结构

gitlab.com/myproject/
  modules/
    itemcategory/
      - itemcategorymanager.go
      - itemcategorymanager_test.go
  
  dataacess/
    model/
      - itemcategorymodel.go

核心代码实现

itemcategorymanager.go

package itemcategory

import "gitlab.com/myproject/dataacess/model"

type ItemCategoryDM interface{
    AddToDB(...) error
    WithTransaction(toExecute func(dm *model.ItemCategoryModel) error) error
}

type ItemCategoryManager struct {
    itemCategoryDM ItemCategoryDM
}

func (m ItemCategoryManager) AddItemCategoryToDB(...) {
  if err := m.itemCategoryDM.WithTransaction(
    func(dm *model.ItemCategoryModel) error {
      // 业务逻辑:调用model的AddToDB,再执行其他操作
      dm.AddToDB(...)
      return m.DoOtherStuff(...)
    }
  ); err != nil {
    // 错误处理
  }
}

func (m ItemCategoryManager) DoOtherStuff(...) error {
  // 其他业务逻辑
}

itemcategorymodel.go

package model

type ThirdPartyORM interface {
    WithTransactionDB(func(txn ThirdPartyORM) error) error
}

type ItemCategoryModel struct {
  orm ThirdPartyORM
}

func NewItemCategoryModel(orm ThirdPartyORM) *ItemCategoryModel {
  return &ItemCategoryModel{orm: orm}
}

func (m ItemCategoryModel) AddToDB(...) error {
  // ORM写入逻辑
}

func (m ItemCategoryModel) WithTransaction(toExecute func(dm *ItemCategoryModel) error) error {
  return m.orm.WithTransactionDB(func(txn ThirdPartyORM) error {
    return toExecute(NewItemCategoryModel(txn))
  })
}

遇到的问题

测试itemcategory.AddItemCategoryToDB方法时,WithTransaction的闭包依赖*model.ItemCategoryModel,但model层未定义接口无法直接mock;若将闭包参数改为*ItemCategoryDM会导致循环依赖(model包需导入业务层接口);移接口到其他包不符合项目规范(接口归属使用方),求可行测试方案。

尝试的测试代码

myModel := &model.ItemCategoryModel{}
itemCategoryDM := NewMockItemCategoryDM(mockController)
itemCategoryDM.
  EXPECT().
  WithTransaction(gomock.Any()).
  Do(func(toExecute func(dm *model.ItemCategoryModel) error) {
    err := toExecute(myModel)
    assert.NoError(t, err)
  }).
  Return(nil).
  Times(1)

itemCategoryDM.EXPECT().DoOtherStuff(...).Return(nil).Times(1)

无法mockmyModel.AddToDB(...)的调用行为与返回值。

解决方案:在业务层定义model操作的最小接口

遵循Go接口设计规范(接口定义在使用它的包中),在itemcategory包中定义仅包含业务所需方法的接口,无需修改model包代码,即可实现mock。

步骤1:在itemcategory包中添加最小接口

修改itemcategorymanager.go,新增针对model操作的接口:

package itemcategory

import "gitlab.com/myproject/dataacess/model"

// 仅定义业务代码中用到的model方法
type ItemCategoryDBActions interface {
    AddToDB(...) error
}

type ItemCategoryDM interface{
    AddToDB(...) error
    // 修改闭包参数为接口类型
    WithTransaction(toExecute func(dm ItemCategoryDBActions) error) error
}

步骤2:调整业务逻辑中的闭包参数

修改AddItemCategoryToDB中的闭包签名,适配新接口:

func (m ItemCategoryManager) AddItemCategoryToDB(...) {
  if err := m.itemCategoryDM.WithTransaction(
    // 参数改为ItemCategoryDBActions接口
    func(dm ItemCategoryDBActions) error {
      dm.AddToDB(...)
      return m.DoOtherStuff(...)
    }
  ); err != nil {
    // 错误处理
  }
}

model.ItemCategoryModel会自动实现ItemCategoryDBActions接口(因为它有AddToDB方法),无需修改model包代码。

步骤3:编写可mock的测试代码

使用Gomock创建ItemCategoryDBActions的mock实例,在测试中控制AddToDB的行为:

func TestAddItemCategoryToDB(t *testing.T) {
    mockCtrl := gomock.NewController(t)
    defer mockCtrl.Finish()

    // Mock ItemCategoryDBActions接口,控制AddToDB的行为
    mockDBActions := NewMockItemCategoryDBActions(mockCtrl)
    mockDBActions.EXPECT().AddToDB(...).Return(nil).Times(1)

    // Mock ItemCategoryDM接口
    mockItemCategoryDM := NewMockItemCategoryDM(mockCtrl)
    mockItemCategoryDM.EXPECT().WithTransaction(gomock.Any()).
        Do(func(toExecute func(dm ItemCategoryDBActions) error) {
            // 传入mock的DBActions实例执行闭包
            err := toExecute(mockDBActions)
            assert.NoError(t, err)
        }).Return(nil).Times(1)

    // Mock DoOtherStuff方法
    mockItemCategoryDM.EXPECT().DoOtherStuff(...).Return(nil).Times(1)

    // 初始化Manager并调用测试方法
    manager := ItemCategoryManager{itemCategoryDM: mockItemCategoryDM}
    manager.AddItemCategoryToDB(...)
}

方案优势

  1. 符合Go接口设计规范:接口定义在业务层(使用方),无需model包导入任何业务层代码,避免循环依赖
  2. 无需修改model包代码,侵入性极低
  3. 可精准mock业务所需的model方法,测试逻辑完全可控

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 22:02:59