Testing Golang测试模拟功能最佳实践

Testing Golang测试模拟功能最佳实践,testing,go,Testing,Go,我正在为我的代码开发一些测试(使用测试包),我想知道在测试函数中模拟函数的最佳方法是什么: 我应该将函数作为参数传递吗? 在这种情况下,如果该函数调用另一个函数呢?我是否应该将第一个和第二个函数作为参数传递给测试函数 注意:有些函数是在对象(即someObj.Create())上调用的,并使用HTTP API调用 更新澄清: 示例:函数 func f1() error { ... //some API call } func (s *SomeStruct) f2() error { r

我正在为我的代码开发一些测试(使用测试包),我想知道在测试函数中模拟函数的最佳方法是什么:

我应该将函数作为参数传递吗? 在这种情况下,如果该函数调用另一个函数呢?我是否应该将第一个和第二个函数作为参数传递给测试函数

注意:有些函数是在对象(即someObj.Create())上调用的,并使用HTTP API调用

更新澄清:

示例:函数

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
}