无法测试 Golang CLI 工具的输出

Unable to test a Golang CLI tool's output

我有一个用 Go 编写的 cli 工具,它产生以下输出:

  Command: config get
      Env: int
Component: foo-component

Unable to find any configuration within Cosmos (http://api.foo.com) for foo-component.

我想在测试中验证此输出。

我写的(没通过)测试如下:

package command

import (
    "testing"

    "github.com/my/package/foo"
)

type FakeCliContext struct{}

func (s FakeCliContext) String(name string) string {
    return "foobar"
}

func ExampleInvalidComponentReturnsError() {
    fakeBaseURL := "http://api.foo.com"
    fakeCliContext := &FakeCliContext{}
    fakeFetchFlag := func(foo.CliContext) (map[string]string, error) {
        return map[string]string{
            "env":       "int",
            "component": "foo-component",
        }, nil
    }

    GetConfig(*fakeCliContext, fakeFetchFlag, fakeBaseURL)

    // Output:
    //   Command: config get
    //       Env: int
    // Component: foo-component
    //
    // Unable to find any configuration within Cosmos (http://api.foo.com) for foo-component.
}

大部分代码都在创建我注入到我的函数调用中的假对象 GetConfig

实际上 GetConfig 没有 return 值,只是文本打印到标准输出的副作用。

所以我使用 Example<NameOfTest> 格式来尝试验证输出。

但是我运行 go test -v刚回来的时候是:

=== RUN   ExampleInvalidComponentReturnsError
exit status 1
FAIL    github.com/my/package/thing 0.418s

有人知道我可能遗漏了什么吗?

我发现如果我在上面的 'Example' 之后添加一个额外的测试,例如称为 Test<NameOfTest> (但实际上相同的代码保持一致),那么这实际上会显示运行测试时函数的输出到我的标准输出:

func TestInvalidComponentReturnsError(t *testing.T) {
    fakeBaseURL := "http://api.foo.com"
    fakeCliContext := &FakeCliContext{}
    fakeFetchFlag := func(utils.CliContext) (map[string]string, error) {
        return map[string]string{
            "env":       "int",
            "component": "foo-component",
        }, nil
    }

    GetConfig(*fakeCliContext, fakeFetchFlag, fakeBaseURL)
}

上面的示例测试在执行 go test -v 时将显示以下输出:

=== RUN   TestInvalidComponentReturnsError
  Command: config get
      Env: int
Component: foo-component

Unable to find any configuration within Cosmos (http://api.foo.com) for foo-component.
exit status 1
FAIL    github.com/bbc/apollo/command   0.938s

好的,这个问题的解决方案是部分架构和部分 removal/refactor 代码。

  1. 我从 c​​li 命令包中提取了私有函数,因此它们成为 public 单独函数中的函数

  2. 我重构了代码以便注入所有依赖项,这让我可以模拟这些对象并验证是否调用了预期的方法

  3. 现在私有函数在一个包中并生成 public,我可以在 cli 上下文之外专门测试这些东西

  4. 最后,我删除了 os.Exit 的使用,因为那是一个难以处理的噩梦,而且并不是真正必要的