在Go中实现测试模拟的首选方法是什么?

时间:2017-08-08 18:49:55

标签: go

我正在Go中构建一个简单的CLI工具,它充当各种密码存储(Chef Vault,Ansible Vault,Hashicorp Vault等)的包装器。这部分是为了熟悉Go。

在处理这个问题时,我遇到了一个我正在编写测试的情况,发现我需要为许多事情创建Dim Value1 Dim Value2 Dim Value3 'Etc. GetSomeValues MyConnectionString, "TABLE_NAME", "ROW_ID = 1", _ "FIELD_NAME_1", Value1, _ "FIELD_NAME_2", Value2, _ "FIELD_NAME_3", Value3, ... Etc. ,只是为了能够模拟依赖项。因此,为了测试,一个相当简单的实现似乎有一堆抽象。

但是,我最近在阅读The Go Programming Language并找到了一个例子,他们以下列方式模拟了它们的依赖关系。

interfaces

因此,为了测试,我们将这个具体实现存储在一个变量中,然后我们可以在测试中重新声明变量并让它返回我们需要的。

否则,我会为此创建func Parse() map[string]string { s := openStore() // Do something with s to parse into a map… return s.contents } var storeFunc = func openStore() *Store { // concrete implementation for opening store } // and in the testing file… func TestParse(t *testing.T) { openStore := func() { // set contents of mock… } parse() // etc... } (尽管目前只有一个实现)并将其注入interface方法。这样,我们可以模拟它进行测试。

所以我的问题是:每种方法的优点和缺点是什么?何时更适合为模拟目的创建接口,而不是将具体函数存储在变量中以便在测试中重新声明?

3 个答案:

答案 0 :(得分:1)

没有"正确的方式"回答这个问题。

说了这些之后,我发现interface方法比定义函数变量并将其设置为测试更通用,更清晰。

以下是对原因的一些评论:

  • 如果您需要模拟多个函数,则function variable方法无法很好地扩展(在您的示例中,它只是一个函数)。

  • interface更明确了注入函数/模块的行为,而不是最终隐藏在实现中的函数变量。

  • interface允许您注入一个带有状态(结构)的类型,这可能对配置模拟行为很有用。

你当然可以依赖"函数变量"处理简单案例并使用"界面"对于更复杂的功能,但是如果你想保持一致并只使用一种方法我会选择"接口"。

答案 1 :(得分:1)

出于测试目的,我倾向于使用您描述的模拟方法而不是创建新接口。其中一个原因是,AFAIK有no direct ways to identify which structs implement an interface,如果我想知道模拟是否做得对,这对我来说很重要。

这种方法的主要缺点是变量本质上是一个包级别的全局变量(即使它未被导出)。因此,声明全局变量的所有缺点都适用。

在测试中,一旦测试完成,您肯定希望使用deferstoreFunc重新分配回其原始的具体实现。

var storeFunc = func *Store {
    // concrete implementation for opening store
}

// and in the testing file…
func TestParse(t *testing.T) {
    storeFuncOriginal := storeFunc
    defer func() {
        storeFunc = storeFuncOriginal
    }()

    storeFunc := func() {
        // set contents of mock…
    } 

    parse()

    // etc...
}

顺便说一句,var storeFunc = func openStore() *Store将无法编译。

答案 2 :(得分:0)

我以不同的方式解决问题。给定

function Parse(s Store) map[string] string{
  // Do stuff on the interface Store
}

你有几个好处:

  1. 您可以根据需要使用模拟或存储库。
  2. Imho,代码变得更加透明。仅签名就表明需要Store实施。并且,打开Store时,代码不需要因错误处理而受到污染。
  3. 代码文档可以更简洁。
  4. 然而,这使得一些事情非常明显:Parse是一个可以附加到商店的功能,这很可能比解析商店更有意义。