🐍
Cobraのサブコマンドに対するテストを書きたい
GoでCLIアプリケーションを簡単に作れてしまうCobra。
サブコマンドに対するユニットテストを書こうしたら苦戦したので、解決方法を紹介します。
環境
- Go:
go 1.23.4 - Cobra:
github.com/spf13/cobra v1.8.1
[準備] サブコマンドの追加
Cobraのインストール方法やGetting startedについては公式ドキュメントをご覧ください。
それでは、サブコマンドを追加しましょう。
ここでは例として hoge というサブコマンドを追加します。
cmd/hoge.go
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
func init() {
rootCmd.AddCommand(hogeCmd)
}
var hogeCmd = &cobra.Command{
Use: "hoge",
Short: "Simple command to print 'Hello, Hoge!'",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Hello, Hoge!")
},
}
それに対するテストコードを書いてみました。
cmd/hoge_test.go
// ❌ダメだった例
package cmd
import (
"bytes"
"testing"
)
func TestHogeCmd(t *testing.T) {
buf := new(bytes.Buffer)
hogeCmd.SetOut(buf)
hogeCmd.Execute()
expected := "Hello, Hoge!\n"
output := buf.String()
if output != expected {
t.Errorf("expected %q, but got %q", expected, output)
}
}
テストを実行してみます。
go test ./cmd/...
--- FAIL: TestHogeCmd (0.00s)
hoge_test.go:17: expected "Hello, Hoge!\n", but got ""
FAIL
FAIL awesome-tool/cmd 0.464s
FAIL
テストコードは一見良さそうなのに、なぜかoutputが取得できません。
原因
原因は2箇所ありました。
-
fmt.Println()で出力している。 -
init()でrootCmd.AddCommand(hogeCmd)している。
解決方法
まず、fmt.Println() を cmd.Println() に置き換えます。それによって、テストコードでSetOut() で指定した出力先に書き込まれます。普通にコマンドを実行したときは標準出力になります。
cmd/hoge.go
Run: func(cmd *cobra.Command, args []string) {
- fmt.Println("Hello, Hoge!")
+ cmd.Println("Hello, Hoge!")
},
次に、AddCommand() の解決方法ですが2通りあります。
方法1: rootCmdから切り離す
init() で AddCommand しているので、逆に RemoveCommand してあげます。
cmd/hoge_test.go
func TestHogeCmd(t *testing.T) {
+ rootCmd.RemoveCommand(hogeCmd)
buf := new(bytes.Buffer)
hogeCmd.SetOut(buf)
hogeCmd.Execute()
}
方法2: rootCmdから実行する
あるいは、いっそのことルートコマンドから実行してあげてもOKです。その場合サブコマンドを引数として追加してあげる必要があります。
cmd/hoge_test.go
func TestHogeCmd(t *testing.T) {
buf := new(bytes.Buffer)
- hogeCmd.SetOut(buf)
- hogeCmd.Execute()
+ rootCmd.SetOut(buf)
+ rootCmd.SetArgs([]string{"hoge"}) // サブコマンドを引数として追加する
+ rootCmd.Execute()
...
}
修正して再実行
テストが通りました 🎉
go test ./cmd/...
ok awesome-tool/cmd 0.409s
まとめ
Cobraのサブコマンドに対するテストコードの書き方を2通り紹介しました。
これでTDD的にCLIツールを開発できますね ✌️
Discussion