Unit testing Golang使用动态函数调用模拟函数

Unit testing Golang使用动态函数调用模拟函数,unit-testing,go,mocking,Unit Testing,Go,Mocking,我对围棋还很陌生,还在学习围棋中的东西是如何工作的,所以我一直在研究围棋的测试方法,以及在过去的几周里模拟是如何工作的,我发现的大部分信息都是基于具体的函数 例如,一切都是一个函数,它要么作为接收器传递,要么作为参数传递,然而,我面临的问题是,我的函数使用开关盒来确定应该调用哪个函数,因此它不是从外部传递的 func(n*Notification)Notify(m Message)错误{ 开关,开关服务{ 案例“松弛”: 放松,放松 s、 User=m.User s、 主机=m.主机 s、 Pr

我对围棋还很陌生,还在学习围棋中的东西是如何工作的,所以我一直在研究围棋的测试方法,以及在过去的几周里模拟是如何工作的,我发现的大部分信息都是基于具体的函数

例如,一切都是一个函数,它要么作为接收器传递,要么作为参数传递,然而,我面临的问题是,我的函数使用开关盒来确定应该调用哪个函数,因此它不是从外部传递的

func(n*Notification)Notify(m Message)错误{
开关,开关服务{
案例“松弛”:
放松,放松
s、 User=m.User
s、 主机=m.主机
s、 Provider=m.Provider
s、 SystemUser=m.SystemUser
返回s.SlackSend(n.Url)
违约:
返回错误。新建(代码。代码5)
}
}
上面的代码就是我想要测试的函数的样子,我很难弄清楚如何模拟
SlackSend()
函数

我遇到过一些文章说我应该在测试文件中编写函数,当我对函数的功能感到满意时,我应该编写真正的代码。对我来说,这听起来并不正确,因为对我来说,我感觉我在两个地方有相同的代码,测试只使用测试中的一个,我可以更改真实的代码并破坏它,而不被测试人员检测到

我主要在Python上工作,并且我习惯于使用Mock/MagicMock之类的东西,它们可以拦截函数调用并在运行时替换,因此如果我没有完全理解Go的TDD方法,请提前道歉

如果有人想知道,这就是测试代码的样子:

type MockSlack结构{
*放松,放松
}
func(s*MockSlack)SlackSend(url字符串)错误{
如果url!“”{
归零
}否则{
返回错误。新建(“url为空”)
}
}
func TestNotify(t*testing.t){
m:=消息{}
n:=通知{
服务:“松弛”,
Url:“https://dummy.io",
}
如果错误:=n.Notify(m);错误!=nil{
t、 Errorf(“SlackSend,应为:%s,已为:%s”,“nil”,err.Error())
}
}
显然,
MockSlack
结构没有生效,因为它没有真正传入任何地方

如果有人对我如何嘲笑这件事或我应该做什么有任何建议,我将不胜感激

提前谢谢

更新: 背景


这不是某种web服务器/应用程序。它是一个SSH身份验证插件,因此它将是一个服务器端应用程序。至于Notify方法,它是映射器的用途。因此,它可以调用Slack、MS团队、AWS SNS,这为呼叫方提供了较少的处理条件,并且它发送通知的方式和位置由
Notify
方法决定。

如果您无法更改
Notify
方法使其易于测试。一种选择是考虑使用。以下是为
*net.Dialer
提供的:

func main(){
var d*net.Dialer//必须是指向的指针,因为'Dial'有一个指针接收器
monkey.PatchInstanceMethod(reflect.TypeOf(d),“Dial”,func(*net.Dialer,*net,uu,u字符串)(net.Conn,错误){
返回零,fmt.Errorf(“不允许拨号”)
})
_,err:=http.Get(“http://google.com")
fmt.Println(err)//获取http://google.com: 不允许拨号
}

警告:在测试环境之外使用它是不安全的。有几种方法可以简化测试,例如使用回调或实现类似于示例的接口,但这需要修改方法
Notify
,使其可测试。你能这样做吗?花一些时间——比如说4个小时——阅读stdlib的测试。您将看到许多适用于Go的测试模式。经典的“嘲弄”并不奏效。使用赝品,尤其是存根是很常见的,效果很好,而模仿则不然。像您这样的mock倾向于测试实现细节,而不是功能。启动一个net/http/httptest.Server,让Notify实际进行http调用。对于大多数来自其他语言的人来说,这听起来很疯狂,但它可以更好地测试您的内容。这还允许您测试所有错误路径,例如错误的凭据、错误的URL、无法解析的主机或超时是如何由您的代码处理的。你的“嘲笑”测试并没有真正测试Notify的有趣内容。如果
n.Service
是一个接口而不是字符串…可能该接口名为
Sender
…并且您的
Notification
结构是使用构造函数
NewNotification
创建的,该构造函数接受
Sender
接口参数并返回
&通知{Service:sender}
…然后在测试中,您可以将一个假发送者传递给构造函数…结果结构的Notify func将调用sender Send()func。当然,不同类型的发送者可以有不同的实现。“我可能会那样做。”沃尔克谢谢你的建议,我会调查的。这部分是我的错,我没有给出应用程序应该做什么的背景。这不是一种web服务器。它是一个SSH身份验证插件,因此它将是一个服务器端应用程序。因此,您建议的一些东西实际上可以用于
SlackSend
方法,它实际上执行HTTP调用。至于
Notify
方法,它是映射器的用途。所以它可以称为Slack、MS团队、AWS SNS。