Go でコードカバレッジを取得する

## ローカル環境でコードカバレッジを確認する

まずテストを記述する必要がある. [How to Write Go Code](https://golang.org/doc/code.html#Testing) を参考にして,

```go:feature/greet.go
func Greet(name string, lang string) string {
	switch lang {
	case "en":
		return fmt.Sprintf("Hello, %s.", name)
	case "ja":
		return fmt.Sprintf("こんにちは, %s.", name)
	}
	return "..."
}
```

という関数に対して,

```go:feature/greet_test.go
type TestCase struct {
	Name     string
	Language string
}

func TestGreet(t *testing.T) {
	cases := []struct {
		in       TestCase
		expected string
	}{
		{TestCase{Name: "World", Language: "en"}, "Hello, World."},
		{TestCase{Name: "Kazumasa", Language: "ja"}, "こんにちは, Kazumasa."},
	}
	for _, c := range cases {
		actual := Greet(c.in.Name, c.in.Language)
		if actual != c.expected {
			t.Errorf("Greet(%q, %q) == %q, expect %q",
				c.in.Name, c.in.Language, actual, c.expected)
		}
	}
}
```

というようなテストを書いてみる. (完全なソースファイルは [ここ](https://github.com/kkohtaka/go-scaffold) にある.)

テストを用意したところで, `go test` を `-cover` オプション付きで実行すると, カバレッジが出力される.

```bash
$ go test -cover ./feature/
ok      github.com/kkohtaka/go-scaffold/feature 0.006s  coverage: 75.0% of statements
```

普通はどの行がカバーされていないかが知りたいと思うので HTML で出力してみる. そこで, `go test` の `-coverprofile` オプションでカバレッジプロファイルをファイルへ出力し, `go tool cover` で HTML へ変換する.

```bash
$ go test -coverprofile=cover.out ./feature/
ok      github.com/kkohtaka/go-scaffold/feature 0.006s  coverage: 75.0% of statements
$ go tool cover -html=cover.out -o cover.html
$ open cover.html
```

出力された HTML のサンプルは [このような感じ](https://drone.io/github.com/kkohtaka/go-scaffold/files/coverage.html).

## CI と組み合わせてコードカバレッジを共有する

ローカル環境でコードカバレッジを確認するだけでなく, 他の開発者とコードカバレッジを共有する方法として, [Codecov](https://codecov.io) と [Coveralls](https://coveralls.io) を試してみた. また, CI サービスとしては [Travis CI](travis-ci.org) を利用した. (同じ要領で [Drone.io](https://drone.io/) でも動作を確認した.)

流れとしては,

1. CI 環境で必要なパッケージをインストール
2. テストを実行してカバレッジプロファイルを出力
3. Codecov, Coveralls それぞれの用意しているスクリプトで結果をアップロード

となる. .travis.yml は下記のような感じ.

```:.travis.yml
before_install:
# Codecov に必要なファイルをインストール
- go get github.com/onsi/gomega
- go get github.com/onsi/ginkgo
# Coveralls に必要なファイルをインストール
- go get github.com/mattn/goveralls
# 両方に必要なファイルをインストール
- go get golang.org/x/tools/cmd/cover

script:
# テストを実行してカバレッジプロファイルを出力
- ./go_test.sh

after_success:
# Codecov へ結果をアップロード
- bash <(curl -s https://codecov.io/bash)
# Coveralls へ結果をアップロード
- $GOPATH/bin/goveralls -coverprofile=coverage.txt -covermode=count -service=travis-ci -repotoken $COVERALLS_TOKEN
```

注意しなければいけない点として, `go test -cover` が複数の Go パッケージのテストをサポートしていないので, カバレッジの取得対象が複数のパッケージを含んでいる場合には各々のカバレッジ結果をマージする必要がある. ここでは下記のようなスクリプトを記述することで対応した. (完全なソースファイルは [ここ](https://github.com/kkohtaka/go-scaffold) にある.)

```bash:go_test.sh
echo "mode: $COVERMODE" > $COVERAGE_FILE

for PKG in $(go list ./...); do
  go test -v -coverprofile=$TMP_FILE -covermode=$COVERMODE $PKG
  if [ -f $TMP_FILE ]; then
    # カバレッジ結果の先頭の "mode: ..." という行を削除
    cat $TMP_FILE | tail -n +2 >> $COVERAGE_FILE
    rm $TMP_FILE
  fi
done
```

Codecov, Coveralls それぞれでの出力結果は下記で確認できる.

- **Codecov**: https://codecov.io/gh/kkohtaka/go-scaffold
- **Coveralls**: https://coveralls.io/github/kkohtaka/go-scaffold

## 参考

- [How to Write Go Code - Testing](https://golang.org/doc/code.html#Testing)
- [The Go Blog - The cover story](https://blog.golang.org/cover)
- [codecov/example-go](https://github.com/codecov/example-go)
- [Go - Coveralls](https://coveralls.zendesk.com/hc/en-us/articles/201342809-Go)