テストを追加する

これでコードが安定した状態になったので (実によくできました)、テストを追加しましょう。開発中にコードをテストすることで、変更を加える際に紛れ込むバグを発見できます。このトピックでは、Hello 関数にテストを追加します。

Go の組み込みの単体テストサポートにより、開発を進めながらテストを行うのが簡単になります。特に、命名規則、Go の testing パッケージ、および go test コマンドを使用することで、テストをすばやく記述して実行できます。

  1. greetings ディレクトリに、greetings_test.go というファイルを作成します。

    ファイル名の末尾を _test.go にすることで、go test コマンドにこのファイルにテスト関数が含まれていることを伝えます。

  2. greetings_test.go に、次のコードを貼り付けてファイルを保存します。
    package greetings
    
    import (
        "testing"
        "regexp"
    )
    
    // TestHelloName calls greetings.Hello with a name, checking
    // for a valid return value.
    func TestHelloName(t *testing.T) {
        name := "Gladys"
        want := regexp.MustCompile(`\b`+name+`\b`)
        msg, err := Hello("Gladys")
        if !want.MatchString(msg) || err != nil {
            t.Errorf(`Hello("Gladys") = %q, %v, want match for %#q, nil`, msg, err, want)
        }
    }
    
    // TestHelloEmpty calls greetings.Hello with an empty string,
    // checking for an error.
    func TestHelloEmpty(t *testing.T) {
        msg, err := Hello("")
        if msg != "" || err == nil {
            t.Errorf(`Hello("") = %q, %v, want "", error`, msg, err)
        }
    }
    

    このコードでは、以下のことを行います。

    • テストするコードと同じパッケージにテスト関数を実装します。
    • greetings.Hello 関数をテストする 2 つのテスト関数を作成します。テスト関数名は TestName の形式であり、Name は特定のテストについて何かを説明します。また、テスト関数は testing パッケージの testing.Tへのポインタをパラメータとして受け取ります。このパラメータのメソッドを使用して、テストからレポートやログを記録します。
    • 2 つのテストを実装する
      • TestHelloNameHello 関数を呼び出し、関数が有効な応答メッセージを返すことができるはずの name 値を渡します。呼び出しがエラーを返したり、予期しない応答メッセージ (渡した名前が含まれていないもの) を返したりした場合は、t パラメータの Errorf メソッドを使用して、コンソールにメッセージを出力します。
      • TestHelloEmpty は空の文字列で Hello 関数を呼び出します。このテストは、エラー処理が機能していることを確認するために設計されています。呼び出しが空ではない文字列を返したり、エラーを返さなかったりした場合は、t パラメータの Errorf メソッドを使用して、コンソールにメッセージを出力します。
  3. greetings ディレクトリのコマンドラインで、go test コマンドを実行してテストを実行します。

    go test コマンドは、テストファイル (名前が _test.go で終わる) 内のテスト関数 (名前が Test で始まる) を実行します。-v フラグを追加すると、すべてのテストとその結果を一覧表示する詳細な出力を得ることができます。

    テストは成功するはずです。

    $ go test
    PASS
    ok      example.com/greetings   0.364s
    
    $ go test -v
    === RUN   TestHelloName
    --- PASS: TestHelloName (0.00s)
    === RUN   TestHelloEmpty
    --- PASS: TestHelloEmpty (0.00s)
    PASS
    ok      example.com/greetings   0.372s
    
  4. greetings.Hello 関数を壊して、失敗するテストを表示します。

    TestHelloName テスト関数は、Hello 関数のパラメータとして指定した名前の戻り値をチェックします。失敗するテスト結果を表示するには、greetings.Hello 関数を変更して、名前が含まれなくなるようにします。

    greetings/greetings.go で、次のコードを Hello 関数の代わりに貼り付けます。ハイライトされた行が、name 引数が誤って削除されたかのように、関数が返す値を変更していることに注意してください。

    // Hello returns a greeting for the named person.
    func Hello(name string) (string, error) {
        // If no name was given, return an error with a message.
        if name == "" {
            return name, errors.New("empty name")
        }
        // Create a message using a random format.
        // message := fmt.Sprintf(randomFormat(), name)
        message := fmt.Sprint(randomFormat())
        return message, nil
    }
    
  5. greetings ディレクトリのコマンドラインで、go test を実行してテストを実行します。

    今回は、-v フラグなしで go test を実行します。出力には、失敗したテストの結果のみが含まれます。これは、多くのテストがある場合に役立ちます。TestHelloName テストは失敗するはずです。TestHelloEmpty は引き続き成功します。

    $ go test
    --- FAIL: TestHelloName (0.00s)
        greetings_test.go:15: Hello("Gladys") = "Hail, %v! Well met!", <nil>, want match for `\bGladys\b`, nil
    FAIL
    exit status 1
    FAIL    example.com/greetings   0.182s
    

次の (そして最後の) トピックでは、コードをコンパイルしてインストールし、ローカルで実行する方法を説明します。