LoginSignup
5
2

More than 3 years have passed since last update.

【Go】Golandでテストから書く開発

Last updated at Posted at 2020-05-28

前書き

  • 私のPC & OS

image.png

前準備

プロジェクト作る

  • Golandにて、File -> New -> Project...

image.png

ディレクトリ作成

今回はサンプルとして型変換処理を作成します。
以下の通りディレクトリを作成

- goalng-test-driven-dev(root)
  ∟ pkg
    ∟ convert

処理の定義を行う

後ほどテストを記述する際に、Golandが補完してくれて便利なので、先に定義してしまいましょう。

  1. /pkg/convertにGoファイルを作成。
  2. 今回はstring型へ型変換処理を行う関数を作成
  3. 引数の型は自由とし、戻り値の型はstringとする。
  4. とりあえずコンパイルが通る様に無理やりstringを返却する。
convert.go
package convert

func ToString(v interface{}) (string) {
    return ""
}

テスト駆動で開発してみる

テスト書く

test.gif

Golandはテストファイルの自動生成が便利です。
以下の通り利用しましょう

  1. テストしたいfuncやファイル内にフォーカスした状態で、Cmd + Shift + T
  2. Test for functionTest for file等、自動生成したい範囲を選択
  3. 以下の通り自動生成されたら、// TODO:にケースを追記していく
  4. 今回はとりあえず、期待値としてstring,bool,intを受け取る様にした。
convert_test.go
package convert

import "testing"

func TestToString(t *testing.T) {
    type args struct {
        v interface{}
    }
    tests := []struct {
        name string
        args args
        want string
    }{
        {name: "String", args: args{v: "hoge"}, want: "hoge"},
        {name: "Int", args: args{v: 1},want: "1"},
        {name: "Bool", args: args{v: true}, want: "true"},
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            if got := ToString(tt.args.v); got != tt.want {
                t.Errorf("ToString() = %v, want %v", got, tt.want)
            }
        })
    }
}

テストを実行する

テストを書いたらすぐに実行しましょう。
もちろん、まだ処理を書いていないのでエラーが発生するはずですね。

  • convert_test.goを右クリックし、Run 'convert_test.go'を選択
    貼り付けた画像_2020_05_28_19_35.png

  • 結果を確認、ちゃんとエラーで落ちてますね!
    image.png

処理を実装する

それでは、テストが通る様に実装をしていきましょう
テストの期待値を満たす様に、型別にstringへ変換してreturnする様にします。

convert.go
package convert

import (
    "strconv"
)

func ToString(v interface{}) string {
    switch vt := v.(type) {
    case string:
        return vt
    case int:
        return strconv.Itoa(vt)
    case bool:
        return strconv.FormatBool(vt)
    default:
        return ""
    }

}

再実行する

全てのテストをPASSしました!
image.png

追加実装する場合

基本的にはやることは変わりません。
1. テストに追記
2. テストFailedを確認
3. 処理を実装
4. テストの再実行
5. テストが通る様になるまで2~4を繰り返す

例) エラーハンドリングを追加する

先ほどの関数に対して、以下の通り期待動作を足します。
string,int,bool以外の型がきた場合はエラーを返却する

テストに追記

convert_test.go
package convert

import "testing"

func TestToString(t *testing.T) {
    type args struct {
        v interface{}
    }
    tests := []struct {
        name string
        args args
        want string
        wantError bool // エラーを期待するか
    }{
        {name: "String", args: args{v: "hoge"}, want: "hoge", wantError: false},
        {name: "Int", args: args{v: 1},want: "1", wantError: false},
        {name: "Bool", args: args{v: true}, want: "true", wantError: false},
        {name: "Other", args: args{v: 123.0}, want: "", wantError: true},
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := ToString(tt.args.v)
            // エラーを期待しているにもかかわらず発生しない
            if err == nil && tt.wantError {
                t.Error("No error occurred.")
                return
            }
            // エラーを期待していないにもかかわらず発生した
            if err != nil && !tt.wantError {
                t.Error("An error has occurred.")
                t.Error(err.Error())
                return
            }
            if got != tt.want {
                t.Errorf("ToString() = %v, want %v", got, tt.want)
            }
        })
    }
}

コンパイルを通すために最低限実装

関数の戻り値の数が変更されてしまっているため、コンパイルが落ちてしまいます。
テストを実行するため、最低限実装してあげましょう。

convert.go
package convert

import (
    "strconv"
)

func ToString(v interface{}) (string, error) { // errorを追加
    switch vt := v.(type) {
    case string:
        return vt, nil // とりあえずnilを返却
    case int:
        return strconv.Itoa(vt), nil
    case bool:
        return strconv.FormatBool(vt), nil
    default:
        return "", nil
    }


}

テストFailedを確認

image.png

エラーを発生させる処理のみテストが落ちています。
それ以外のケースについてはnilで合っているので通ってますね。

処理を実装

package convert

import (
    "errors"
    "strconv"
)

func ToString(v interface{}) (string, error) {
    switch vt := v.(type) {
    case string:
        return vt, nil
    case int:
        return strconv.Itoa(vt), nil
    case bool:
        return strconv.FormatBool(vt), nil
    default:
        return "", errors.New("Error")
    }

}

テストの再実行

image.png

無事に通りましたね!

カバレッジをとってみる

カバレッジ 【 coverage 】 カバー率

ソフトウェア開発において、出来上がったプログラムのテストをする際に、どの程度をテスト対象とする(ことができる)かをカバレッジ(テストカバレッジ)という。

コード量が増えてくると、ちゃんとソース上の各ステップを網羅できてるか確認したくなりますよね。

image.png

こちらで実行をすると、以下の通り、カバー率がでます。

image.png

もし網羅できていない部分がある場合、ソースの該当箇所が赤くマーキングされて、テストを行っていない箇所が明確になります。
image.png

後書き

テストファイルの自動生成が便利ですね。
細かい単位でテストを行うことで、自然とテストが容易なコードを書ける様になり、疎結合なコードを書いていきたくなりますね。

5
2
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
5
2