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实例:
- 第一个实例设置了
Send方法的预期,但没有被传递给Router - 第二个全新的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
相关产品推荐
相关产品推荐

