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

Go语言Mock不符合预期:Send方法调用未被识别问题排查

问题分析与解决

错误日志

assert: mock: I don't know what to return because the method call was unexpected.
        Either do Mock.On("Send").Return(...) first, or remove the Send() call.
        This method was unexpected:
                Send(services.ConfirmAccountEmail,repository.User)

调用真实API时邮件发送正常,mailer.Send可正常触发;但测试时使用MockMailer出现上述错误,调试显示已进入Send方法。

相关代码片段

Mailer接口

type Mailer interface {
    Send(email Email, receiver User) error
}

Email同样是接口类型

Mailer接口实现

type EmailDelivery struct {
    Config Config
}

func (service EmailDelivery) Send(email Email, receiver User) error {
    // 业务代码
}

Router实现

type Router struct {
    api         Api
    db          *gorm.DB
    redisClient *redis.Client
    config      Config
    mailer      Mailer
}

func NewRouter(api Api, db *gorm.DB, redisClient *redis.Client, config Config, mailer Mailer) Router {
    return Router{api, db, redisClient, config, mailer}
}

func (router *Router) AddRoutes() {
    router.api.AddRouteWithCors("/register", "POST", WithRequestLog(router.RegisterHandler))
    // 其他路由注册
}

func (router *Router) RegisterHandler(w http.ResponseWriter, r *http.Request) {
    // 业务逻辑处理...

    email := NewConfirmAccountEmail(router.config, "Email subject", "email_template")
    router.mailer.Send(email, user)
}

Api初始化路由逻辑

func (api *Api) InitializeRoutes(mailer Mailer) *Router {
    // 初始化前置逻辑...
    router := NewRouter(*api, api.db, api.redisClient, config, mailer)
    router.AddRoutes()
    // 其他初始化逻辑...
}

测试代码

MockMailer定义

type MockMailer struct {
    mock.Mock
}

func (m *MockMailer) Send(email Email, receiver User) error {
    args := m.Called(email, receiver)
    return args.Error(0)
}

测试逻辑(原错误版本)

mailer := new(MockMailer)
mailer.On("Send", mock.Anything, mock.Anything).Return(nil)

// 执行调用Send的代码
api := NewApi(config.ServerPort, db, nil)

mailer := new(MockMailer) // 问题出在这里:重新创建了实例!
router := api.InitializeRoutes(mailer)

req, err := http.NewRequest(http.MethodPost, "/register", bytes.NewReader(body))
w := httptest.NewRecorder()
router.RegisterHandler(w, req)

res := w.Result()
defer res.Body.Close()

mailer.AssertExpectations(t)
// 其他断言

问题根源

你在测试中重复创建了MockMailer实例:

  1. 第一个实例设置了Send方法的预期,但没有被传递给Router
  2. 第二个全新的MockMailer实例被传给了Router,但没有配置任何方法预期
    当代码调用Send时,实际使用的是第二个无配置的Mock实例,因此触发了"方法调用未预期"的错误。调试显示进入Send方法是因为第二个Mock的Send确实被执行了,但它没有对应的On配置。

解决方案

删掉测试中重复创建MockMailer的代码,确保设置预期的实例和传递给Router的是同一个:

mailer := new(MockMailer)
mailer.On("Send", mock.Anything, mock.Anything).Return(nil)

api := NewApi(config.ServerPort, db, nil)
router := api.InitializeRoutes(mailer) // 传递同一个Mock实例

req, err := http.NewRequest(http.MethodPost, "/register", bytes.NewReader(body))
w := httptest.NewRecorder()
router.RegisterHandler(w, req)

res := w.Result()
defer res.Body.Close()

mailer.AssertExpectations(t)
// 其他断言

额外提示

如果之后遇到接口类型匹配的问题,可使用mock.MatchedBy指定精确匹配规则,比如验证传入的Email是否为ConfirmAccountEmail类型:

mailer.On("Send", mock.MatchedBy(func(e Email) bool {
    _, ok := e.(*ConfirmAccountEmail)
    return ok
}), mock.Anything).Return(nil)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 01:09:58