2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

単体テストにおいて前処理と後処理をする場合が多い。

例えばDBのデータを作成/削除する、一時ファイルを作成/削除するなどテスト対象の関数やメソッドの責務のみに集中するためには、テストの前処理と後処理を行う必要がある。

Goのテストにおいての前処理と後処理にはTestMain関数を用いる。

一方で各テスト関数において前処理と後処理を行う方法として、defert.Cleanupがある。

この挙動の差をサンプルコードを用いて確認する。

deferとt.Cleanupの違い

deferは関数の終了時に実行される処理を登録する。

一方でt.Cleanupはテスト関数の後処理を登録する関数で、テスト関数が終了するときに登録した関数が実行される。

サンプルのテストコード

さて問題です。

次のようなテストコードが実行されるとどのように表示されるか。

package main_test

import (
	"fmt"
	"testing"
)

func TestMain(m *testing.M) {
	fmt.Println("before tests")
	m.Run()
	fmt.Println("after tests")
}

func TestDeferVsCleanup(t *testing.T) {
	t.Cleanup(func() {
		fmt.Println("Cleanup: テストのクリーンアップ処理")
	})

	helperCleanup(t)

	// deferの登録
	defer fmt.Println("defer: 関数の終了時に実行")

	fmt.Println("テスト処理の実行")

	t.Run("subtest", func(t *testing.T) {
		defer fmt.Println("defer: サブテストの終了時に実行")
		t.Cleanup(func() {
			fmt.Println("Cleanup: サブテストのクリーンアップ処理")
		})
		fmt.Println("サブテストの実行")
	})
}

func helperCleanup(t *testing.T) {
	t.Helper()
	t.Cleanup(func() {
		fmt.Println("Cleanup: ヘルパー関数のクリーンアップ処理")
	})
}

答え

$ go test -v
before tests
=== RUN   TestDeferVsCleanup
テスト処理の実行
=== RUN   TestDeferVsCleanup/subtest
サブテストの実行
defer: サブテストの終了時に実行
Cleanup: サブテストのクリーンアップ処理
defer: 関数の終了時に実行
Cleanup: ヘルパー関数のクリーンアップ処理
Cleanup: テストのクリーンアップ処理
--- PASS: TestDeferVsCleanup (0.00s)
    --- PASS: TestDeferVsCleanup/subtest (0.00s)
PASS
after tests

解説

Goのテストやベンチマーク測定が実行されると、最初にTestMain(m *testing.M)関数が実行される。

メインのgoroutineで実行され、前処理と後処理を行うことができる。

m.Run()を実行することで各テスト関数が実行されるため befer testsが最初に、after tests が最後に表示される。

TestDeferVsCleanup関数を順に見ていく。

t.Cleanup、及びt.CleanupをコールしているhelperCleanup はテスト関数の後処理を登録する関数で、テスト関数が終了するときに登録した関数が実行される。

関数の終了時に実行されるdeferは、関数の終了時に実行されるがt.Cleanupよりも先に実行される。

次にfmt.Println("テスト処理の実行")が書かれているのでこれは最初に表示される。

続いてサブテストが実行され、fmt.Println("サブテストの実行")が書かれているため次はこれが表示される。

サブテスト内にdeferとt.Cleanupが登録されているため、サブテストの後処理がdeferで登録されたfmt.Println("defer: サブテストの終了時に実行") -> t.Cleanupで登録されたfmt.Println("Cleanup: サブテストのクリーンアップ処理")の順に表示される。

サブテストの後処理が終わると、TestDeferVsCleanup関数の後処理が実行される。

TestDeferVsCleanup関数の後処理はdeferで登録されたfmt.Println("defer: 関数の終了時に実行") -> t.Cleanupで登録されたfmt.Println("Cleanup: ヘルパー関数のクリーンアップ処理")の順に表示される。

t.Clearupで登録された関数は、登録された順と逆順に実行される。

参考: https://pkg.go.dev/testing#hdr-Main

まとめ

defer, t.Cleanupはテスト関数の後処理を書け、サブテスト内やヘルパー関数内でも登録した場合の動きを見た。

これらの性質を理解して、テストコードを書く際に使い分けていきましょう!

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?