LoginSignup
7
4

More than 3 years have passed since last update.

cweill/gotestsでTable Driven Testsをらくらく実行

Posted at

はじめに

Table Driven Tests(テーブルドリブンテスト。以下、TDT)とは、入力値と期待値などをもったテストケースを用意し、順次実行していくテスト手法です。
ここでいうTableとはエクセルのようなスプレッドシート、すなわち表のことを指し、テストケースを網羅した表を用意するようにテストコードを用意していきます。
公式でも使われています。
https://github.com/golang/go/wiki/TableDrivenTests

TDTの例

例として、挨拶メッセージを返すメソッドをテストしてみましょう。

引数の時刻によって「おはよう」「こんにちは」「こんばんは」の3種類のメッセージのうちどれか1種類が返されます。
条件と期待結果は以下のとおりです。

Input Output
05:00-11:59 おはよう
12:00-17:59 こんにちは
18:00-04:59 こんばんは

メソッドは以下のとおり。

// greeter.go

package main

import "time"

func Greet(time time.Time) string {
    hour := time.Hour()
    switch {
    case 5 <= hour && hour <= 11:
        return "おはよう"
    case 12 <= hour && hour <= 17:
        return "こんにちは"
    case (18 <= hour && hour <= 23) || (0 <= hour && hour <= 4):
        return "こんばんは"
    }
    panic("Unknown Hour")
}

ボイラープレートの生成をcweill/gotestsに任せる

公式例のようにテストを書いていこう!としても、なかなか面倒です。
そこでcweill/gotestsを活用していきます。

gotestsはメソッドシグネチャを読み取り、テスト用のボイラープレートを自動生成してくれます(大感謝)。

// 2019/10/08 最新版
go get -u github.com/cweill/gotests@v1.5.3

// greeter.goのテストコードを生成
gotests -w -all greeter.go

テストケースを追加していく

以下のようなgreeter_test.goが生成されたはずです。
nameがテスト名、argsがGreetの引数、wantが期待結果を指します。

// greeter_test.go

package main

import (
    "testing"
    "time"
)

func TestGreet(t *testing.T) {
    type args struct {
        time time.Time
    }
    tests := []struct {
        name string
        args args
        want string
    }{
        // TODO: Add test cases.
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            if got := Greet(tt.args.time); got != tt.want {
                t.Errorf("Greet() = %v, want %v", got, tt.want)
            }
        })
    }
}

実際に// TODO: Add test cases.の箇所にケースを追加していきましょう。

境界値を意識すると、テストはこんな感じになるかと思います(便宜的に3ケースだけ掲載)。

Name Input Output
05:00 expects おはよう 05:00 おはよう
11:59 expects おはよう 11:59 おはよう
12:00 expects こんにちは 12:00 こんにちは

コードで網羅すると、テストメソッドは以下のようになります。

package main

import (
    "testing"
    "time"
)

func TestGreet(t *testing.T) {
    type args struct {
        time time.Time
    }

    tests := []struct {
        name string
        args args
        want string
    }{
        // おはよう
        {
            name: "05:00 expects おはよう",
            args: args{
                time: time.Date(2019, 1, 1, 5, 0, 0, 0, time.Local),
            },
            want: "おはよう",
        },
        {
            name: "11:59 expects おはよう",
            args: args{
                time: time.Date(2019, 1, 1, 11, 59, 59, 59, time.Local),
            },
            want: "おはよう",
        },
        // こんにちは
        {
            name: "12:00 expects こんにちは",
            args: args{
                time: time.Date(2019, 1, 1, 12, 0, 0, 0, time.Local),
            },
            want: "こんにちは",
        },
        {
            name: "17:59 expects こんにちは",
            args: args{
                time: time.Date(2019, 1, 1, 17, 59, 59, 59, time.Local),
            },
            want: "こんにちは",
        },
        // こんばんは
        {
            name: "18:00 expects こんばんは",
            args: args{
                time: time.Date(2019, 1, 1, 18, 0, 0, 0, time.Local),
            },
            want: "こんばんは",
        },
        {
            name: "23:59 expects こんばんは",
            args: args{
                time: time.Date(2019, 1, 1, 23, 59, 59, 59, time.Local),
            },
            want: "こんばんは",
        },
        {
            name: "00:00 expects こんばんは",
            args: args{
                time: time.Date(2019, 1, 1, 0, 0, 0, 0, time.Local),
            },
            want: "こんばんは",
        },
        {
            name: "04:59 expects こんばんは",
            args: args{
                time: time.Date(2019, 1, 1, 4, 59, 59, 59, time.Local),
            },
            want: "こんばんは",
        },
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            if got := Greet(tt.args.time); got != tt.want {
                t.Errorf("Greet() = %v, want %v", got, tt.want)
            }
        })
    }
}

Run

書き終わったら、あとはテストを走らせるだけ。

input/outputが網羅されて、わかりやすいテストになりました。

Running tool: /usr/local/go/bin/go test -timeout 30s github.com/mshrwtnb/sandbox -run ^(TestGreet)$ -v

=== RUN   TestGreet
=== RUN   TestGreet/05:00_expects_おはよう
=== RUN   TestGreet/11:59_expects_おはよう
=== RUN   TestGreet/12:00_expects_こんにちは
=== RUN   TestGreet/17:59_expects_こんにちは
=== RUN   TestGreet/18:00_expects_こんばんは
=== RUN   TestGreet/23:59_expects_こんばんは
=== RUN   TestGreet/00:00_expects_こんばんは
=== RUN   TestGreet/04:59_expects_こんばんは
--- PASS: TestGreet (0.00s)
    --- PASS: TestGreet/05:00_expects_おはよう (0.00s)
    --- PASS: TestGreet/11:59_expects_おはよう (0.00s)
    --- PASS: TestGreet/12:00_expects_こんにちは (0.00s)
    --- PASS: TestGreet/17:59_expects_こんにちは (0.00s)
    --- PASS: TestGreet/18:00_expects_こんばんは (0.00s)
    --- PASS: TestGreet/23:59_expects_こんばんは (0.00s)
    --- PASS: TestGreet/00:00_expects_こんばんは (0.00s)
    --- PASS: TestGreet/04:59_expects_こんばんは (0.00s)
PASS
ok      github.com/mshrwtnb/sandbox 1.309s
Success: Tests passed.

以上

7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4