func TestAbc(t *testing.T) {
// Save current function and restore at the end:
old := zSomeFunc
defer func() { zSomeFunc = old }()
zSomeFunc = func() int {
// This will be called, do whatever you want to,
// return whatever you want to
return 1
}
// Call the tested function
abc()
// Check expected behavior
}
import (
z "x.y.z"
)
//this should replicate the type of function z from x.y.z
type operation func() resp
func wrappedAbc(op operation) {
....
resp := op()
....
}
func Abc() {
wrappedAbc(z)
}
func functionThatUsesGlobalFunction(
format string,
args ...interface{},
) string {
//
// skip fansy logic...
//
// call out to a global function in fmt package
return fmt.Sprintf(format, args...)
}
5条答案
按热度按时间zsohkypk1#
是的,通过一个简单的重构。创建一个函数类型的
zSomeFunc
变量,用z.SomeFunc
初始化,然后让你的包调用它而不是z.SomeFunc()
:在测试中,你可以给
zSomeFunc
分配另一个函数,一个在测试中定义的函数,并且执行测试想要它做的任何事情。例如:
参见相关/可能重复:在Go语言中使用覆盖信息测试os. exit场景(coveralls.io/Goveralls)
k5hmc34c2#
你可以做的一件事是:
在你的测试文件中你会这样做。
但是要确保以并发方式执行此操作,如果有多个
TestXxx
函数调用abc
或设置someFunc
,则最好使用带有someFunc
字段的struct
。iswrvxsc3#
有函数指针和猴子修补它是这样做的一个。但是当你有多个函数要模仿时,你会有很多函数指针,不必要的,你将不得不只使用函数指针调用函数。
更好和推荐的想法是有一个接口,并使您的函数成为实现该接口的结构体的一部分。一旦这样做了,您就可以使用一些不错的工具生成mock。
我一直在用这个:
您可以通过以下方式进行安装:
mnemlml84#
虽然创建包级变量是一个可行的选项,但它有一些限制。仅举几例:
1.它不鼓励使用
t.Parallel()
运行并行测试,因为模拟函数的不同行为可能存在争用条件。1.这是危险的,因为同一包中的未来开发者可能意外地更新该全局变量的实现。
另一种方法是把你想模拟的方法作为参数传递给函数来实现可测试性。2在我的例子中,我已经有很多客户调用这个方法,因此,我想避免违反现有的契约。3所以,我最终创建了一个 Package 函数。
例如:
现在测试实际的逻辑,您将测试对
wrappedAbc
而不是abc
的调用,并且您将向其传递一个模拟的operation
。这将允许您测试所有的业务逻辑,同时不违反与方法Abc
的当前客户端的API合同。ghg1uchk5#
mockcompose
使用一种允许您生成模拟类的方法,您可以指示mockcompose
包含您选择的依赖项闭包(任何从其他包导入的函数)。同时,它会生成一个带有本地覆盖的主题函数的克隆副本,以便您进行测试。您可以使用go generate
嵌入代码生成过程,因此,请确保克隆副本始终与代码更改保持同步。假设您有一个函数
functionThatUsesGlobalFunction
,它将Sprintf
导入到包fmt
中。您的目标是使用被模拟的包
fmt
中的Sprintf
测试functionThatUsesGlobalFunction
。为此,您可以使用
mockcompose
配置go generate
,如下所示:mocks.go
go generate mockcompose
将为您生成管道类,使您能够编写如下测试代码:请检查this以了解详细信息。