无法测试 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 代码。
我从 cli 命令包中提取了私有函数,因此它们成为 public 单独函数中的函数
我重构了代码以便注入所有依赖项,这让我可以模拟这些对象并验证是否调用了预期的方法
现在私有函数在一个包中并生成 public,我可以在 cli 上下文之外专门测试这些东西
最后,我删除了 os.Exit 的使用,因为那是一个难以处理的噩梦,而且并不是真正必要的
我有一个用 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 代码。
我从 cli 命令包中提取了私有函数,因此它们成为 public 单独函数中的函数
我重构了代码以便注入所有依赖项,这让我可以模拟这些对象并验证是否调用了预期的方法
现在私有函数在一个包中并生成 public,我可以在 cli 上下文之外专门测试这些东西
最后,我删除了 os.Exit 的使用,因为那是一个难以处理的噩梦,而且并不是真正必要的