Help us understand the problem. What is going on with this article?

Goでテスト時にflag引数に任意の値を渡す

More than 1 year has passed since last update.

こんにちわ
ゴリラです

Goでテストを書く時、コマンドライン引数に任意の値を渡したい事があったので、
そのやり方をメモとして残しておきます。

flag引数

Goでは -arg value という感じで、flagパッケージ引数を定義することができます。

var (
    name    = flag.String("name", "gorilla", "your name")
    age     = flag.Int("age", 10, "your age")
    isHuman = flag.Bool("isHuman", false, "are your human?")
)

func echo() string {
    return fmt.Sprintf("your status: name:%s, age:%d, isHuman:%v", *name, *age, *isHuman)
}

やり方

この引数の値をテスト時に任意の変更して、分岐を網羅したいときがあります。
そういった場合は、flagパッケージで用意されている flag.CommandLine.Set 関数を使うことで、
任意の値をセットすることができます。

func TestEcho(t *testing.T) {
    tests := []struct {
        name    string
        age     string
        isHuman string
        want    string
    }{
        {name: "gorilla", age: "26", isHuman: "false", want: "your status: name:gorilla, age:26, isHuman:false"},
        {name: "dog", age: "10", isHuman: "false", want: "your status: name:dog, age:10, isHuman:false"},
        {name: "bob", age: "30", isHuman: "true", want: "your status: name:bob, age:30, isHuman:true"},
    }

    for _, test := range tests {
        flag.CommandLine.Set("name", test.name)
        flag.CommandLine.Set("age", test.age)
        flag.CommandLine.Set("isHuman", test.isHuman)

        got := echo()
        if test.want != got {
            t.Fatalf("want %s, but got %s", test.want, got)
        }
    }
}

CommandLine.Set の引数は keyvalue があり、ともにstringですが、
value は内部でよしなに型変換してくれるので、intやboolも全てはstringで渡す必要があります。

まとめ

flag引数とそれ以外の引数をまとめて持っているのが flag.FlagSet という構造体です。

type FlagSet struct {
    // Usage is the function called when an error occurs while parsing flags.
    // The field is a function (not a method) that may be changed to point to
    // a custom error handler. What happens after Usage is called depends
    // on the ErrorHandling setting; for the command line, this defaults
    // to ExitOnError, which exits the program after calling Usage.
    Usage func()

    name          string
    parsed        bool
    actual        map[string]*Flag
    formal        map[string]*Flag
    args          []string // arguments after flags
    errorHandling ErrorHandling
    output        io.Writer // nil means stderr; use out() accessor
}

args がflag引数以外の引数が入り、 formal がflag引数が入っています。
深くは読んでいないのですが、interfaceを使用して、valueをparseしたりしているので、
シンプルだけど勉強になりそうなので、興味ある方はソースを読んでみてください。

gorilla0513
https://twitter.com/gorilla0513
https://github.com/skanehira
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away