Golang测试模拟功能最佳实践

时间:2016-04-18 08:26:30

标签: testing go

我正在为我的代码开发一些测试(使用 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 个答案:

答案 0 :(得分:3)

作为一般准则,函数不是非常可模仿的,因此我们最好的兴趣是模拟实现某个接口的结构,该接口可以传递给函数来测试不同的代码分支。请参阅下面的基本示例。

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,它只是一个如何进行测试的模板var text = "This is another msg!"; var pubkey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwyTZf5gRWJdEevtK7sJSz14lhs1Jw7+aFhGtr4cbDGxdiXH8J+BwuYmBc6QFMhRw7AeYcgkx9zPb3SICzr+oK17RMA6T66dH+GPXp75LFUmfONfk2JdSeO80mMODGctSuefWDvoQ24Cq0Bz+ysrhP7hRqvJso5a0GMNPwt8ErtWfz4HZjSsaaZ7gXga2h5dq1OTcGNfevkDN9CJtFW/0Wwb/F6cnXngVHE41rsN4POUB3IWcX2CrCGxSraa+xsT/P7AJ8HRJ4wcjl9G2K/rlHJ8ZXZKlIuWwEzx0/F0IjE+S93tLpDgt6YJxjWqYqjL2uuJAGmEU323+PWA3jFTC+QIDAQAB"; var encrypt = new JSEncrypt(); encrypt.setPublicKey(pubkey); var ciphertext = encrypt.encrypt(text); console.log("ciphertext : " + ciphertext); var decrypt = new JSEncrypt(); decrypt.setPrivateKey($("#privkey").val()); var plaintext = decrypt.decrypt(ciphertext); console.log("plaintext : " + plaintext);

答案 1 :(得分:0)

我不确定你在这里要做什么,但我会解释如何在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

中指定

您可以详细了解有关测试herehere

的信息