🔍

Goでテストしてカバレッジ計測

2022/02/26に公開

はじめに

平下CTO@sweeepです。Go言語でのテスト方法やカバレッジ計測などをこちらをサンプルに解説します。→ GoでファイルのValidation方法など
本記事の内容です。

  • Goのテスト
  • テストのsetup/teardown
  • カバレッジ計測
  • テスト実行結果

Goのテスト

まずxxx_test.goというファイルを作成し、testingとassertをimportします(packageはテスト対象と同じ)。
テストモジュールにはTestifyを使用しています。

Goテストモジュール Testifyをつかってみた | DevelopersIO https://dev.classmethod.jp/articles/go-testify/
以下のようなパッケージが含まれており、テストコードが簡潔にかけます。
assert package
require package
mock package
suite package

utils/file/file_test.go
package file

import (
	"testing"
	"github.com/stretchr/testify/assert"
)

https://github.com/stretchr/testify/

関数名にはTestXXXと先頭に[Test]をつけます。

utils/file/file_test.go
func TestCheckFileSize(t *testing.T) {
    ...
}

以下はテスト対象のファイルサイズチェック処理です。

utils/file/file.go
func CheckFileSize(size int64) (int64, error) {
	if size > MaxSize { // MaxSizeより大きい場合はエラー
		return size, fmt.Errorf("size: %v error: %w", size, ErrFileSize)
	}
	return size, nil
}

テスト処理は下記記事を参考にAAA(Arrange/Action/Assert)を意識してテスト準備、テストの実行、テストの検証と書くようにしています。

それは、テストコードを読みやすくするための指針です。AAA は arrange, act, assert の略語で、arrange は『準備』、act は『実行』、assertは『検証』を表します。

準備はテスト用のデータの作成、実行はテストしたい関数やメソッドを実行すること、検証は実行の結果得られた値や変化が期待したものであるかどうかを確認することになります。

AAA を意識して単体テストを書く - ペパボテックブログ

https://tech.pepabo.com/2021/08/23/writing-unit-test-with-aaa/

utils/file/file_test.go
func TestCheckFileSize(t *testing.T) {
	// arrange
	f, _ := os.Open("../../sample/image.jpg")
	defer func() {
		_ = f.Close()
	}()

	// action
	fi, _ := f.Stat()
	_, actual := CheckFileSize(fi.Size())

	// assert
	assert.ErrorIs(t, nil, actual)
}
...

テストのsetup/teardown

テスト前にsetupでコンフィグやenv設定したり、テスト終了後にteardownでテスト用のデータのクリア処理したいことがあると思います。以下のようにsetupとteardown定義し、TestMainで呼び出します。

utils/file/file_test.go
// テストの開始・終了
func TestMain(m *testing.M) {
	setup()
	ret := m.Run()
	if ret == 0 {
		teardown()
	}
	os.Exit(ret)
}

// テスト初期設定
func setup() {
	log.Println("test start!")
}

// テスト終了処理
func teardown() {
	log.Println("test end!")
}

実行すると以下の様にテストケースの開始と終了にsetupとteardownが呼ばれます。

file $ go test -v -coverpkg=. ./...
2022/02/26 15:59:26 test start!
...
2022/02/26 15:59:26 test end!

テストの実行とカバレッジ計測

テストの実行は以下のコマンドで実行します。[-coverpkg]オプションでカバレッジを計測します。

$ go test -v -coverpkg=. ./...

カバレッジ計測結果は以下のように表示されます(80%ですね)。

coverage: 80.0% of statements in .
ok      github.com/hirac1220/go-file-validate/utils/file        0.119s  coverage: 80.0% of statements in .

どの処理がカバーされていないか確認したい場合は[-coverprofile]でプロファイル作成し、htmlへ変換し出力します。

$ go test -v -coverprofile=cover_file.out  -coverpkg=./...
$ go tool cover -html=cover_file.out -o cover_file.html

すると添付のようにカバーされている処理は緑で、カバーされていない処理が赤く表示されます(サンプルではエラー処理がカバーできていないですね)。

テスト実行結果

コマンド打つのがちょっとまんどいので、Makefileでテスト実施しています。

utils/file/Makefile
test_file:
	go test -v -coverpkg=. ./...

test_cover_file:
	go test -v -coverprofile=cover_file.out  -coverpkg=./...
	go tool cover -html=cover_file.out -o cover_file.html

テスト実行結果は以下のようになり、すべてPASSしてカバレッジが80%というのがわかります(失敗するとFAILが表示されます)

file $ make test_file
go test -v -coverpkg=. ./...
2022/02/26 15:59:26 test start!
=== RUN   TestCheckFileSize
--- PASS: TestCheckFileSize (0.00s)
=== RUN   TestValidateJPEG
--- PASS: TestValidateJPEG (0.00s)
=== RUN   TestValidateJPG
--- PASS: TestValidateJPG (0.00s)
=== RUN   TestValidatePNG
--- PASS: TestValidatePNG (0.00s)
=== RUN   TestJPGExtToContentType
--- PASS: TestJPGExtToContentType (0.00s)
=== RUN   TestJPEGExtToContentType
--- PASS: TestJPEGExtToContentType (0.00s)
=== RUN   TestPNGExtToContentType
--- PASS: TestPNGExtToContentType (0.00s)
PASS
coverage: 80.0% of statements in .
2022/02/26 15:59:26 test end!
ok      github.com/hirac1220/go-file-validate/utils/file        0.119s  coverage: 80.0% of statements in .

まとめ

今回はGo言語でのテストとカバレッジ計測ついて解説しました。
テストがあるおかげで、大幅な仕様変更時やリファクタリング時にも思い切った変更ができるようになったと感じています。
カバレッジ数値目標ですが75%ぐらいがよいのかな、と思っています。ただ、カバレッジを目標に無理くりテストコードを書くのは本末転倒な気がします。適切なテストコードとマニュアルテストの組合せで品質を作り込むことが大事かな、と思っています。
使用したサンプルはこちらのGitHubレポジトリにあります。
https://github.com/hirac1220/go-file-validate

GitHubで編集を提案

Discussion