我试图在我的测试文件中模拟 Go 函数 os.GetEnv()
,以便我可以获得特定环境变量的所需值。
比如我已经定义了。abc := os.GetEnv("XYZ_URL")
在这里,我应该能够获得变量 abc
所需的值。我还有几个地方有 GetEnv
函数。
如果有人可以在没有任何 Go 框架帮助的情况下给我一个解决方法,那将非常有帮助。
最佳答案
首先,您不能模拟该功能。您只能模拟作为接口(interface)公开的内容。
其次,你可能不需要。从广义上讲,模拟被过度使用,应尽可能避免。
测试环境变量时,您有四个常规选项:
type OS interface {
Getenv(string) string
}
type defaultOS struct{}
func (defaultOS) Getenv(key string) string {
return os.Getenv(key)
}
// Then in your code, replace `os.Getenv()` with:
myos := defaultOS{}
value := myos.Getenv("XYZ_URL")
在您的测试中,创建一个满足接口(interface)的自定义实现,但提供您测试所需的值。这种方法对某些事情很有用(比如包装
time
包),但对于 os.Getenv
来说可能是一种不好的方法。os.Getenv
,而只是传入值。 示例,而不是: func connect() (*DB, error) {
db, err := sql.Connect(os.Getenv("XYZ_URL"), ...)
/* ... */
return db, err
}
用:func connect(url string) (*DB, error) {
db, err := sql.Connect(url, ...)
/* ... */
return db, err
}
从某种意义上说,这只是“移动”了问题——您可能仍然想测试使用 os.Getenv()
的调用者,但您至少可以减少依赖于这种方法的 API 的表面积,这使得第三种方法更容易.func TestFoo(t *testing.T) {
orig := os.Getenv("XYZ_URL")
os.Setenv("XYZ_URL", "http://example.com")
t.Cleanup(func() { os.Setenv("XYZ_URL", orig) })
/* do your tests here */
}
这种方法确实有局限性。特别是,并行运行多个这些测试是行不通的,因此您仍然希望尽量减少运行的这些测试的数量。这意味着方法 2 和方法 3 相互结合可以非常强大。
var getenv = os.Getenv
/* ... then in your code ... */
func foo() {
value := getenv("XYZ_URL") // Instead of calling os.Getenv directly
}
并在测试中:func TestFoo(t *testing.T) {
getenv = func(string) string { return "http://example.com/" }
/* ... your actual tests ... */
}
这与选项 #3 有许多相同的限制,因为您不能并行运行多个测试,因为它们会发生冲突。关于unit-testing - 模拟 os.GetEnv ("ENV_VAR"),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/66061175/