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

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

はじめに

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

https://github.com/cweill/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.

以上

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
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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
ユーザーは見つかりませんでした