我有一个我正在制作的 cli,它更多地用于学习目的和创建我自己的 cli,它确实有用。无论如何,我正在测试 delete
功能,它工作正常,给了我正确的答案。但是,我不认为这是最佳做法,并且想知道您是否可以让我知道它是否可以。
测试文件
func TestDeleteConfig(t *testing.T) {
err := cm.DeleteConfig()
if err != nil {
t.Errorf("error when deleting the folder: %s", err)
}
usr, err := user.Current()
if err != nil {
t.Errorf("error when getting user current: %s", err)
}
fp := filepath.Join(usr.HomeDir, ".config", "godot", "config.json")
fmt.Println("the path of the config file", fp)
if _, e := os.Stat(fp); !os.IsNotExist(e) {
t.Errorf("error path still exists: %v", e)
}
}
正在测试的功能func DeleteConfig() error {
usr, err := user.Current()
if err != nil {
return err
}
err = os.RemoveAll(filepath.Join(usr.HomeDir, ".config", "godot"))
if err != nil {
return err
}
return nil
}
问题是我不想要 DeleteConfig()
接受任何论据,因为这是一条艰难的道路。我有一个单独的函数用于删除单个文件,其中的帮助也将解决该问题func DeleteFile(p string) error {}
.因此,出于测试目的,我应该只创建一个
foo
目录位于单独的路径(在测试中),删除所述目录,并假设它是否适用于 foo
那么它应该与 godot
一起使用目录?
最佳答案
这有点哲学。
如果您绝对不想 stub /模拟代码的任何部分来代替访问真实文件系统进行测试,那么我们正在谈论您所说的系统或集成测试。这本身很好:例如,您可以在作为 CI 管道的一部分的一次性容器中运行此类测试。
但是使用这种方法,您无法明智地进行所谓的单元测试。
要对您的功能进行单元测试,您需要用“虚拟化”的东西替换它使用的东西。究竟如何做到这一点是一个悬而未决的问题。
方法:make可以覆盖os.RemoveAll
例如,您可以拥有一个包含“删除整个目录”功能的私有(private)全局变量,如下所示:
var removeAll = os.RemoveAll
func DeleteConfig() error {
usr, err := user.Current()
if err != nil {
return err
}
err = removeAll(filepath.Join(usr.HomeDir, ".config", "godot"))
if err != nil {
return err
}
return nil
}
然后为了测试它,您只需使用您自己的实现对函数进行猴子补丁,该实现将具有与 os.RemoveAll
相同的签名。 , 像这样:func TestDeleteConfig(t *testing.T) {
var actualPath string
removeAll = func(path string) {
actualPath = path
return nil
}
defer func() { removeAll = os.RemoveAll }()
DeleteConfig()
if actualPath != expectedPath {
t.Errorf("unexpected path: want %s, got %s", expectedPath, actualPath)
}
}
这种方法还允许测试您的函数如何处理错误:只需将其替换为产生错误的内容,然后检查您的被测函数是否返回了该错误(或以预期的方式包装它,无论如何)。尽管如此,由于多种原因,它的技术含量有点低:
方法:不要硬编码“用户”或“配置”的概念
另一种方法是注意,基本上测试的问题源于您硬编码获取用户的事实。
撇开您为获取配置位置而采取的有缺陷的方法(您应该使用 something which implements the XDG spec ),如果您可以轻松覆盖获取“根”目录(这是您的代码中用户的主目录),您可以轻松地将您的函数定位为对调用
io/ioutil.TempDir
的结果进行操作.所以可能的一种方法是拥有一个接口(interface)类型
type ConfigStore interface {
Dir() string
}
其中Dir()
方法应该返回配置存储的根目录的路径。您的
DeleteConfig()
然后将开始接受 ConfigStore
类型的单个参数,并且在您的程序中,您将拥有它的具体实现,并且在您的测试代码中 - 一个实现相同接口(interface)并管理临时目录的 stub 。方法:全面虚拟化
现在,正在对 bringing filesystem virtualization right into the Go standard library 进行一项工作。 ,但是虽然它还没有,但可以做到这一点的 3rd-party 包已经存在了很长时间,例如,
github.com/spf13/afero
.基本上,它们允许您不使用
os
直接但以某种方式编写所有代码,而不是 os
包它调用实现特定接口(interface)的类型实例上的方法:在生产代码中,该对象是 os
的薄垫片包,并在测试代码中替换为您想要的任何内容; afero
有一个现成的内存 FS 后端 to do this .
关于go - 测试删除目录的最佳实践是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64650931/