Golang勉強メモ②単体テスト

golangの単体テストは、golangの標準パッケージ
`testing`を使用することで作成することができる。
[testing](https://golang.org/pkg/testing/)

# 検証環境のディレクトリ構造
```
golang
├main.go
└person
 ├person.go
 └person_test.go
```


# VSCode上での単体テスト作成方法
テストしたいメソッドの上でコンテキストメニューを開き、
`Go:generate Unit Tests For Function`を選択するとテストコードが生成される。
`testing`は`{テスト対象のソース名}_test.go`というファイルを生成する。

![ezgif.com-video-to-gif (5).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298390/4a130923-41be-a5bc-1c3c-a0819e064406.gif)

```Go:main.go
package main

import (
	"fmt"
	"./person"
)

func main() {
	fmt.Println("start")
	person.InitPerson()

}
```

```Go:person.go
package person

import "fmt"

// Person Recording Persons Score & rank.
type Person struct {
	name    string
	english int
	rank    string
}

// InitPerson doing Initialize Person.
func InitPerson() {
	Bob := Person{"Bob", 60, ""}
	Bob.rank = Score(Bob)
	fmt.Printf("Name:%v\n", Bob.name)
	fmt.Printf("Score:%v\n", Bob.english)
	fmt.Printf("rank:%v\n", Bob.rank)
}

// Score Return rank by score.
func Score(p Person) string {
	if p.english > 80 {
		return "A"
	} else if p.english > 60 {
		return "B"
	} else {
		return "C"
	}
}

```

person.goのScoreメソッドに対して、下記のテストコードが生成される。


```person_test.go
package person

import (
	"testing"
)
func TestScore(t *testing.T) {
	type args struct {
		p Person
	}
	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 := Score(tt.args.p); got != tt.want {
				t.Errorf("Score() = %v, want %v", got, tt.want)
			}
		})
	}
}
```

生成されたコードのstructにテスト対象の値を渡し、テストを実行することもできるが、
今回はスケルトンを使用せず、下記の通りテストコードを作成した。

```go:score_test.go
package person

import (
	"testing"
)

func TestScore_1(t *testing.T) {
	p := Person{"Bob", 60, ""}
	actual := Score(p)
	expected := "C"
	if actual != expected {
		t.Errorf("got %v want %v", actual, expected)
	}
}
```

VSCode上で、メソッド上部に表示される`run test`を実行すると
メソッド単位でテストを実行することができる。
実行すると、下記の結果がターミナルに出力される。

```
Running tool: /usr/local/go/bin/go test -timeout 30s -run ^(TestScore_1)$

PASS
ok  	_/Users/username/golang/golang/person	0.010s
Success: Tests passed.
```

また、テストコードを格納しているディレクトリで
`go test .`を実行すると、ディレクトリ内の全テストを実行する事ができる。
先程のコードに新たに２ケース追加した。
失敗時の動作を確認するためTestScore_3はNGケースを設定している。

```go:person_test.go
package person

import (
	"testing"
)

func TestScore_1(t *testing.T) {
	p := Person{"Bob", 90, ""}
	actual := Score(p)
	expected := "A"
	if actual != expected {
		t.Errorf("got %v want %v", actual, expected)
	}
}

func TestScore_2(t *testing.T) {
	p := Person{"Bob", 80, ""}
	actual := Score(p)
	expected := "B"
	if actual != expected {
		t.Errorf("got %v want %v", actual, expected)
	}
}

//NG case
func TestScore_3(t *testing.T) {
	p := Person{"Bob", 60, ""}
	actual := Score(p)
	expected := "B"
	if actual != expected {
		t.Errorf("got %v want %v", actual, expected)
	}
}
```

上記テストの実行結果は下記の通りである。
失敗すると、正常な結果が得られなかったソースの箇所と、
設定したエラーメッセージがターミナルに表示される。
１箇所エラーが発生した時点で、後続のコードは実行されない。

```
$ go test .
--- FAIL: TestScore_3 (0.00s)
    person_test.go:31: got C want B
FAIL
FAIL    _/Users/username/golang/golang/person 0.006s
FAIL
```

#  カバレッジの取得
`-cover`オプションをつけることで、カバレッジを取得することができる。

```
go test . -cover
ok      _/Users/username/golang/golang/person 0.011s  coverage: 50.0% of statements
```


また、カバレッジをHTMLとして出力することもできる。
`test`の`-coverprofile`オプションで、カバレッジプロファイルをファイルに出力し、
出力したファイルを`go tool cover`でHTMLファイルへ変換する。

```
$go test -coverprofile=cover.out ./
ok      _/Users/username/golang/golang/person 0.007s  coverage: 50.0% of statements
$ go tool cover -html=cover.out -o cover.html
```

カバレッジをhtmlで表示すると下記の通り。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298390/27e66317-018d-ea8a-f5a2-74b6358989d7.png)