如何在 go 中实现 CLI 命令的单元测试
How to implement unit tests for CLI commands in go
我正在启动一个使用 spf13/cobra 的新 OSS CLI 工具。作为 golang 的新手,我很难找出隔离测试命令的最佳方法。谁能给我一个如何测试命令的例子?几个警告:
- 你不能从你的 init 函数return cobra.Command
- cmd 目录中不能有
get_test.go
...我的印象是 golang 最佳实践。
- 我是 golang 的新手,请放轻松 :sweat_smile:
如有错误请指正
这是我要测试的命令:https://github.com/sahellebusch/raider/blob/3-add-get-alerts/cmd/get.go。
乐于接受想法、建议、批评等等。
您可以查看 cobra
本身是如何做到的 - https://github.com/spf13/cobra/blob/master/command_test.go
基本上您可以将实际的命令逻辑(运行 函数)重构为一个单独的函数并测试该函数。您可能想要正确命名您的函数,而不是仅仅调用它 run
。
使用 go 实现 CLI 有多种方法。这是我开发的 CLI 的基本结构,主要受 docker CLI 的影响,我也添加了单元测试。
您首先需要的是将 CLI 作为界面。这将在名为 "cli".
的包中
package cli
type Cli interface {
// Have interface functions here
sayHello() error
}
这将由 2 个 clis 实现:HelloCli(我们真正的 CLI)和 MockCli(用于单元测试)
package cli
type HelloCli struct {
}
func NewHelloCli() *HelloCli {
cli := &HelloCli{
}
return cli
}
这里HelloCli会实现如下的sayHello函数
package cli
func (cli *HelloCli) SayHello() error {
// Implement here
}
同样,在名为test
的包中会有一个模拟cli,它会实现cli接口,它也会实现sayHello函数。
package test
type MockCli struct {
}
func NewMockCli() *HelloCli {
cli := &MockCli{
}
return cli
}
func (cli *MockCli) SayHello() error {
// Mock implementation here
}
现在我将展示如何添加命令。首先我要有主包,这是我要添加所有新命令的地方。
package main
func newCliCommand(cli cli.Cli) *cobra.Command {
cmd := &cobra.Command{
Use: "foo <command>"
}
cmd.AddCommand(
newHelloCommand(cli),
)
return cmd
}
func main() {
helloCli := cli.NewHelloCli()
cmd := newCliCommand(helloCli)
if err := cmd.Execute(); err != nil {
// Do something here if execution fails
}
}
func newHelloCommand(cli cli.Cli) *cobra.Command {
cmd := &cobra.Command{
Use: "hello",
Short: "Prints hello",
Run: func(cmd *cobra.Command, args []string) {
if err := pkg.RunHello(cli, args[0]); err != nil {
// Do something if command fails
}
},
Example: " foo hello",
}
return cmd
}
在这里,我有一个名为 hello
的命令。接下来,我将在名为 "pkg" 的单独包中实现该实现。
package pkg
func RunHello(cli cli.Cli) error {
// Do something in this function
cli.SayHello()
return nil
}
单元测试也将包含在此包中名为 hello_test
.
的文件中
package pkg
func TestRunHello(t *testing.T) {
mockCli := test.NewMockCli()
tests := []struct {
name string
}{
{
name: "my test 1",
},
{
name: "my test 2"
},
}
for _, tst := range tests {
t.Run(tst.name, func(t *testing.T) {
err := SayHello(mockCli)
if err != nil {
t.Errorf("error in SayHello, %v", err)
}
})
}
}
当你执行 foo hello
时,HelloCli
将被传递给 sayHello() 函数,当你 运行 单元测试时,MockCli
将被传递。
我正在启动一个使用 spf13/cobra 的新 OSS CLI 工具。作为 golang 的新手,我很难找出隔离测试命令的最佳方法。谁能给我一个如何测试命令的例子?几个警告:
- 你不能从你的 init 函数return cobra.Command
- cmd 目录中不能有
get_test.go
...我的印象是 golang 最佳实践。 - 我是 golang 的新手,请放轻松 :sweat_smile:
如有错误请指正
这是我要测试的命令:https://github.com/sahellebusch/raider/blob/3-add-get-alerts/cmd/get.go。
乐于接受想法、建议、批评等等。
您可以查看 cobra
本身是如何做到的 - https://github.com/spf13/cobra/blob/master/command_test.go
基本上您可以将实际的命令逻辑(运行 函数)重构为一个单独的函数并测试该函数。您可能想要正确命名您的函数,而不是仅仅调用它 run
。
使用 go 实现 CLI 有多种方法。这是我开发的 CLI 的基本结构,主要受 docker CLI 的影响,我也添加了单元测试。
您首先需要的是将 CLI 作为界面。这将在名为 "cli".
的包中package cli
type Cli interface {
// Have interface functions here
sayHello() error
}
这将由 2 个 clis 实现:HelloCli(我们真正的 CLI)和 MockCli(用于单元测试)
package cli
type HelloCli struct {
}
func NewHelloCli() *HelloCli {
cli := &HelloCli{
}
return cli
}
这里HelloCli会实现如下的sayHello函数
package cli
func (cli *HelloCli) SayHello() error {
// Implement here
}
同样,在名为test
的包中会有一个模拟cli,它会实现cli接口,它也会实现sayHello函数。
package test
type MockCli struct {
}
func NewMockCli() *HelloCli {
cli := &MockCli{
}
return cli
}
func (cli *MockCli) SayHello() error {
// Mock implementation here
}
现在我将展示如何添加命令。首先我要有主包,这是我要添加所有新命令的地方。
package main
func newCliCommand(cli cli.Cli) *cobra.Command {
cmd := &cobra.Command{
Use: "foo <command>"
}
cmd.AddCommand(
newHelloCommand(cli),
)
return cmd
}
func main() {
helloCli := cli.NewHelloCli()
cmd := newCliCommand(helloCli)
if err := cmd.Execute(); err != nil {
// Do something here if execution fails
}
}
func newHelloCommand(cli cli.Cli) *cobra.Command {
cmd := &cobra.Command{
Use: "hello",
Short: "Prints hello",
Run: func(cmd *cobra.Command, args []string) {
if err := pkg.RunHello(cli, args[0]); err != nil {
// Do something if command fails
}
},
Example: " foo hello",
}
return cmd
}
在这里,我有一个名为 hello
的命令。接下来,我将在名为 "pkg" 的单独包中实现该实现。
package pkg
func RunHello(cli cli.Cli) error {
// Do something in this function
cli.SayHello()
return nil
}
单元测试也将包含在此包中名为 hello_test
.
package pkg
func TestRunHello(t *testing.T) {
mockCli := test.NewMockCli()
tests := []struct {
name string
}{
{
name: "my test 1",
},
{
name: "my test 2"
},
}
for _, tst := range tests {
t.Run(tst.name, func(t *testing.T) {
err := SayHello(mockCli)
if err != nil {
t.Errorf("error in SayHello, %v", err)
}
})
}
}
当你执行 foo hello
时,HelloCli
将被传递给 sayHello() 函数,当你 运行 单元测试时,MockCli
将被传递。