Testing Golang测试模拟功能最佳实践
我正在为我的代码开发一些测试(使用测试包),我想知道在测试函数中模拟函数的最佳方法是什么: 我应该将函数作为参数传递吗? 在这种情况下,如果该函数调用另一个函数呢?我是否应该将第一个和第二个函数作为参数传递给测试函数 注意:有些函数是在对象(即someObj.Create())上调用的,并使用HTTP API调用 更新澄清: 示例:函数Testing Golang测试模拟功能最佳实践,testing,go,Testing,Go,我正在为我的代码开发一些测试(使用测试包),我想知道在测试函数中模拟函数的最佳方法是什么: 我应该将函数作为参数传递吗? 在这种情况下,如果该函数调用另一个函数呢?我是否应该将第一个和第二个函数作为参数传递给测试函数 注意:有些函数是在对象(即someObj.Create())上调用的,并使用HTTP API调用 更新澄清: 示例:函数 func f1() error { ... //some API call } func (s *SomeStruct) f2() error { r
func f1() error {
... //some API call
}
func (s *SomeStruct) f2() error {
return f1
}
func f3() error {
return nil
}
func f4() error {
...
err = obj.f2()
...
err = f3()
...
}
对于以上内容:如果我想测试f4,模拟f2和f3的最佳方法是什么
如果我将f2和f3作为参数传递给f4,它将工作,但f2测试的结果如何?我应该将f1传递给f2作为参数吗
如果是这样的话,那么f4的参数中是否也应该有f1呢?一般来说,函数不是非常可模拟的,因此我们最好模拟实现特定接口的结构,该接口可以传递到函数中以测试不同的代码分支。请参见下面的基本示例
package a
type DoSomethingInterface interface {
DoSomething() error
}
func DoSomething(a DoSomethingInterface) {
if err := a.DoSomething(); err != nil {
fmt.Println("error occurred")
return
}
fmt.Println("no error occurred")
return
}
package a_test
import (
"testing"
"<path to a>/a"
)
type simpleMock struct {
err error
}
func (m *simpleMock) DoSomething() error {
return m.err
}
func TestDoSomething(t *testing.T) {
errorMock := &simpleMock{errors.New("some error")}
a.DoSomething(errorMock)
// test that "an error occurred" is logged
regularMock := &simpleMock{}
a.DoSomething(regularMock)
// test "no error occurred" is logged
}
a包
类型dosomething接口{
DoSomething()错误
}
func DoSomething(DoSomething接口){
如果错误:=a.DoSomething();错误!=nil{
fmt.Println(“发生错误”)
返回
}
fmt.Println(“未发生错误”)
返回
}
包a_测试
进口(
“测试”
“/a”
)
类型simpleMock结构{
错误
}
func(m*simpleMock)DoSomething()错误{
返回m.err
}
func TestDoSomething(t*testing.t){
errorMock:=&simpleMock{errors.New(“某些错误”)}
a、 剂量测定法(误差模拟)
//测试是否记录了“发生错误”
regularMock:=&simpleMock{}
a、 剂量测定(常规模拟)
//记录测试“未发生错误”
}
在上面的示例中,您将测试DoSomething
函数和发生的分支,例如,您将为一个测试用例创建一个有错误的mock实例,并创建另一个没有错误的mock实例来测试另一个用例。各自的情况是测试某个字符串是否已登录到标准输出;在这种情况下,当用错误实例化simpleMock
时,它将是“发生错误”
;当没有用错误实例化simpleMock
时,它将是“未发生错误”
当然,这可以扩展到其他情况,例如,DoSomething
函数实际上返回某种类型的值,您需要对该值进行断言
编辑:
我更新了代码,担心接口存在于另一个包中。请注意,新更新的代码有一个包a
,其中包含被测试的接口和功能,还有一个包a_test
,它只是一个如何进行测试的模板a.DoSomething
我不确定您在这里试图做什么,但我将解释如何在Go中进行测试
假设我们有一个具有以下目录层次结构的应用程序:
root/
pack1/
pack1.go
pack1_test.go
pack2/
pack2.go
pack2_test.go
main.go
main_test.go
我们假设pack2.go
具有您想要测试的功能:
package pack2
func f1() error {
... //some API call
}
func (s *SomeStruct) f2() error {
return f1
}
func f3() error {
return nil
}
func f4() error {
...
err = obj.f2()
...
err = f3()
...
}
到目前为止看起来不错。现在,如果您想测试pack2中的函数,您需要创建一个名为pack2\u test.go
的文件。go中的所有测试文件的名称都类似(packagename_test.go)。现在让我们看看包的典型测试的内部(本例中为pack2_test.go):
让我解释一下。注意,在pack2_test.go中,第一行表示包是pack2
。简而言之,这意味着我们在包pack2
的“范围”内,因此可以调用pack2
中的所有函数,就好像您在pack2
中一样。这就是为什么在Testf*函数中,我们可以从pack2
调用函数。另一件需要注意的事情是导入的包“测试”。这有两方面的帮助:
首先,它提供了一些运行测试的功能。我不想谈那件事。
其次,它有助于确定go test
应该运行的函数
现在来看看函数。测试包中任何带有前缀“test”和参数“t*testing.t”(不需要使用测试功能时可以使用“*testing.t”)的函数都将在运行go test
时执行。您可以使用变量t
来引用我提到的测试功能。您还可以声明不带前缀的函数,并在带前缀的函数中调用它们
因此,如果我转到我的终端并运行go test
,它将执行您想要测试的功能,在pack2\u test.go
您可以了解有关测试的更多信息,当您说测试函数中的模拟函数时,您的确切意思是什么?用一个示例更新了问题。我希望它更清楚。我对测试很陌生,我甚至不能100%确定如何提问,你需要进一步澄清。这些函数在您正在测试的包中吗?f1和f2不在测试包中,f3在(但在实际代码中,f2在测试包中的另一个函数中被调用,因此还有一层)您好,谢谢您的回答。问题是我需要使用的接口不在我需要测试的包中。是否可以在不同的包中实现接口?我正在查找,但找不到示例是的,您仍然可以在另一个包中实现另一个接口。Go不同于Java,因为它不需要显式的“实现”来遵循接口,而实际上更像是在动态语言中键入duck。您所要做的就是为特定的结构使用相同的方法,并且该结构可以用来满足函数所期望的接口。@Ciack404我更新了代码示例,以模板化可能感兴趣的接口代码。如果有帮助,请告诉我。嗨,谢谢你的回复。但是如果package2中的函数正在使用function,会发生什么呢
package pack2
import (
"testing"
"fmt"
)
TestF1(*testing.T) {
x := "something for testing"
f1() // This tests f1 from the package "pact2.go"
}
TestF2(*testing.T) {
y := new(somestruct)
y.f2() // tests f2 from package "pact2.go"
}
TestF3(*testing.T) {
/// some code
f3() // tests f3
}
TestF4(*testing.T) {
/// code
f3() // you get the gist
}