ker*_*707 6 testing unit-testing mocking go
假设我有一个包含以下代码的包:
package paths
type FilePath struct {
PathA string
}
func (c FilePath) GetPathA() string {
if err := PathExists(PathA); err != nil {
return ""
}
return PathA + "foo"
}
func PathExists(p string) error {
// call os and file methods
return err
}
Run Code Online (Sandbox Code Playgroud)
如何模拟 PathExists 依赖项来测试 FilePath?此外,PathExists许多其他包也正在使用该方法。(我愿意接受重构此代码以使其测试友好的建议,请记住以下几点)
我遇到过几种不同的方法,但对我来说,没有一种方法是直观的或惯用的。
PE := PathExists包中有全局变量;在 中GetPathA,调用err := PE(PathA)并在测试PE中用模拟方法覆盖。
问题:如果测试包类似于 paths_test,我将必须导出,PE这也允许包的客户端覆盖它。
在测试中创建PathExists一个字段FilePath并模拟该字段。
问题:客户端在使用该包时,必须初始化PathExists字段,或者我提供一个表单的构造函数NewFilePath(PathtA string)来为我初始化字段。在实际用例中,字段很多,因此这种方法也会失败。
使用接口并将其嵌入到结构中。当客户端使用它时,它会使用实际方法进行初始化并进行测试模拟。
type PathExistser interface{
PathExists(p string) error
}
type FilePath struct{
PathA string
PathExister
}
type Actual struct{}
func (a Actual) PathExists(p string) error {
return PathExists(p)
}
Run Code Online (Sandbox Code Playgroud)
问题:客户端再次需要提供接口的正确实现。
我了解到还有一些方法可以做与上述选项类似的事情,例如使该方法PathExists成为 的参数GetPathA等。所有人都有相同的担忧。基本上,我不希望这个包的用户必须弄清楚什么应该是正确的输入参数以确保结构按预期工作。我也不希望用户覆盖该行为PathExists。
这似乎是一个非常简单的问题,我似乎错过了一些关于测试或模拟的非常有趣的东西。任何帮助将不胜感激,谢谢。
方法名称只是示例。实际上GetPathA或者PathExists会更加复杂。
为了解决您的方法中的问题1.,您可以使用内部包,然后您可以导入该包,paths_test但包的客户端则不能导入。
package paths
import (
// ...
"<your_module_path>/internal/osutil"
)
func PathExists(p string) error {
return osutil.PathExists(p)
}
Run Code Online (Sandbox Code Playgroud)
package osutil
var PathExists = func(p string) error {
// call os and file methods
return err
}
// Use a mutex to make sure that if you have
// multiple tests using mockPathExists and running
// in parallel you avoid the possiblity of a data race.
//
// NOTE that the mutex is only useful if *all* of your tests
// use MockPathExists. If only some do while others don't but
// still directly or indirectly cause the paths.PathExists
// function to be invoked then you still can run into a data
// race problem.
var mu sync.Mutex
func MockPathExists(mock func(p string) error) (unmock func()) {
mu.Lock()
original := PathExists
PathExists = mock
return func() {
PathExists = original
mu.Unlock()
}
}
Run Code Online (Sandbox Code Playgroud)
package paths_test
import (
// ...
"<your_module_path>/internal/osutil"
)
func TestPathExists(t *testing.T) {
unmock := osutil.MockPathExists(myPathExistsMockImpl)
defer unmock()
// do your test
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2285 次 |
| 最近记录: |