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