为了账号安全,请及时绑定邮箱和手机立即绑定

Golang 测试模拟函数最佳实践

Golang 测试模拟函数最佳实践

Go
跃然一笑 2022-01-04 18:51:29
我正在为我的代码开发一些测试(使用testing包),我想知道在测试函数中模拟函数的最佳方法是什么:我应该将函数作为参数传递吗?在这种情况下,如果该函数调用另一个函数呢?我应该将第一个和第二个函数作为测试函数中的参数传递吗?注意:一些函数在对象上调用(即 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 吗?
查看完整描述

2 回答

?
翻阅古今

TA贡献1780条经验 获得超5个赞

作为一般准则,函数不是很容易模拟,因此模拟实现特定接口的结构最符合我们的利益,这些接口可以传递给函数以测试不同的代码分支。请参阅下面的基本示例。


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

}

在上面的示例中,您将测试DoSomething函数和发生的分支,例如。您将为一个测试用例创建一个带有错误的模拟实例,并创建另一个没有错误的模拟实例来测试另一个用例。各自的情况是测试某个字符串是否已经被登录到标准输出;在这种情况下,它将是"error occurred"在simpleMock实例化"no error occurred"时出现错误,何时simpleMock未实例化时出现错误。


这当然可以扩展到其他情况,例如。该DoSomething函数实际上返回某种值,并且您希望对assertion值进行处理。


编辑:


我更新了代码,担心接口存在于另一个包中。请注意,新的更新代码有一个a包含接口和被测功能的包a_test,以及一个仅作为如何进行测试的模板的包a.DoSomething。


查看完整回答
反对 回复 2022-01-04
?
慕盖茨4494581

TA贡献1850条经验 获得超11个赞

我不确定您要在这里做什么,但我将解释如何在 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_test.go. go中的所有测试文件都以类似的方式命名(packagename_test.go)。现在让我们看看一个包的典型测试的内部(在这个例子中是 pack2_test.go):


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

}

让我解释。请注意,在 pack2_test.go 中,第一行表示包是pack2. 简而言之,这意味着我们在包的“范围”中pack2,因此pack2可以像在pack2. 这就是为什么在 Testf* 函数中,我们可以从pack2. 另一件需要注意的是导入包“测试”。这有助于两件事:


首先,它提供了一些运行测试的功能。我不会进入那个。其次,它有助于确定go test应该运行的功能。


现在到功能。测试包中具有前缀“Test”和参数“t *testing.T”(当您不需要使用测试功能时可以使用“*testing.T”)的任何函数将在您执行跑 go test。您可以使用该变量t来引用我提到的测试功能。您还可以声明不带前缀的函数并在带前缀的函数中调用它们。


因此,如果我转到我的终端并运行go test,它将执行您要测试的功能,在pack2_test.go


您可以在此处和此处了解有关测试的更多信息


查看完整回答
反对 回复 2022-01-04
  • 2 回答
  • 0 关注
  • 247 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号