技術共有

単体テストに行く

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Go 言語では、単体テストは標準ライブラリのテスト パッケージを通じて実装されます。このパッケージは、単体テストの作成、実行、管理をシンプルかつ効率的に行うための一連の関数を提供します。

1. ルール

  • テストファイルの命名規則
    Go のテスト ファイルの命名規則は次のとおりです。_test.go 。たとえば、calculator.go ファイルに対応するテスト ファイルは次のとおりです。calculator_test.go

  • テスト関数の命名規則
    テスト関数は次で始まる必要がありますTest で始まり、その後に空でない文字列を続けることができます。たとえば、TestAddTestSubtract 待って。

  • 使用 testing.T アサーションとエラー報告を行う
    テスト関数では、次のように使用します。testing.T パラメータを入力してテストのステータスと出力を管理します。使用できますt.Error*t.Fail* テストの失敗を示し、関連するエラー情報を出力するその他のメソッド。

2. 単体テストの例

2.1 単一のテストケース

電卓関数は電卓パッケージで定義されており、具体的な実装は次のとおりです。

package calculator

func Add(a, b int) int {
	return a + b
}
  • 1
  • 2
  • 3
  • 4
  • 5

現在のディレクトリにテスト ファイル calculator_test.go を作成し、次のようにテスト関数を定義します。

package calculator

import "testing"


func TestAdd(t *testing.T) {
	result := Add(1, 2)
	expected := 3
	if result != expected {
		t.Errorf("Add(1,2) return %d, expected %d", result, expected)
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

この単体テストを実行するには、 go test コマンドを使用します。コマンドラインで、calculator.go と calculator_test.go を含むディレクトリを入力し、実行します。go test、これらの結果は次のとおりです

go test
PASS
ok      modu    0.226s
  • 1
  • 2
  • 3

2.2 複数のテストケース

次のテスト関数を calculator_test.go に追加します。

func TestAdd2(t *testing.T) {
	result := Add(3, 2)
	expected := 3
	if result != expected {
		t.Errorf("Add(1,2) return %d, expected %d", result, expected)
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

出力結果で各テスト ケースの実行をよりよく確認するには、次のようにします。go test -vパラメータを使用して、完全なテスト結果を出力できるようにします。

go test -v
=== RUN   TestAdd
--- PASS: TestAdd (0.00s)
=== RUN   TestAdd2
    calculator_test.go:17: Add(1,2) return 5, expected 3
--- FAIL: TestAdd2 (0.00s)
FAIL
exit status 1
FAIL    modu    0.216s
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2.3 実行中のテスト ケースを指定する

go test -run このコマンドは、指定されたモードに従ってテストを実行できます。このコマンドは、実行するテスト関数を選択するための正規表現をサポートしています。

たとえば、TestAdd2 ユースケースを修正した後、次のように渡します。go tes -run=Add2TestAdd2 テスト ケースのみを実行すると、結果は次のようになります。

go test -run=Add2 -v
=== RUN   TestAdd2
--- PASS: TestAdd2 (0.00s)
PASS
ok      modu    0.198s
  • 1
  • 2
  • 3
  • 4
  • 5

4. いくつかのテストケースをスキップする
新しいテスト機能を追加

func TestAdd3(t *testing.T) {
	if testing.Short() {
        t.Skip("short模式下会跳过该测试用例")
    }
	
	result := Add(3, 2)
	expected := 5
	if result != expected {
		t.Errorf("Add(1,2) return %d, expected %d", result, expected)
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

実行時go test -shortの場合はスキップされますtesting.Short()マークされたテスト ケース、結果は次のとおりです

go test -short -v
=== RUN   TestAdd
--- PASS: TestAdd (0.00s)
=== RUN   TestAdd2
--- PASS: TestAdd2 (0.00s)
=== RUN   TestAdd3
    calculator_test.go:23: short模式下会跳过该测试用例
--- SKIP: TestAdd3 (0.00s)
PASS
ok      modu    0.635s
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3. テストグループとサブテスト

3.1 テストグループとサブテスト

テスト グループとサブテストを通じて、より多くのテスト ケースを追加し、より分かりやすい方法で結果を表示できます。

func TestAdd(t *testing.T) {
	tests := []struct {
		name     string
		x, y     int
		expected int
	}{
		{"Add1", 1, 2, 3},
		{"Add2", 3, 3, 6},
		{"Add3", 4, 5, 8},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			result := Add(tc.x, tc.y)
			if result != tc.expected {
				t.Errorf("Add(%d, %d) returned %d, expected %d", tc.x, tc.y, result, tc.expected)
			}
		})
	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

走るgo test -v、消す

go test -v
=== RUN   TestAdd
=== RUN   TestAdd/Add2
=== RUN   TestAdd/Add3
    calculator_test.go:51: Add(4, 5) returned 9, expected 8
--- FAIL: TestAdd (0.00s)
    --- PASS: TestAdd/Add1 (0.00s)
    --- PASS: TestAdd/Add2 (0.00s)
    --- FAIL: TestAdd/Add3 (0.00s)
FAIL
exit status 1
FAIL    modu    0.190s
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

3.2 並列テスト

Go 言語は本質的に同時実行性をサポートしているため、t.Parallel()ドライバーテストの並列化を実現します。

func TestAdd(t *testing.T) {
	t.Parallel()  // 将 TLog 标记为能够与其他测试并行运行
	// 这里使用匿名结构体定义了若干个测试用例
	// 并且为每个测试用例设置了一个名称
	tests := []struct {
		name     string
		x, y     int
		expected int
	}{
		{"Add1", 1, 2, 3},
		{"Add2", 3, 3, 6},
		{"Add3", 4, 5, 8},
	}

	for _, tc := range tests {
		tc := tc  // 注意这里重新声明tt变量(避免多个goroutine中使用了相同的变量)
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()  // 将每个测试用例标记为能够彼此并行运行
			result := Add(tc.x, tc.y)
			if result != tc.expected {
				t.Errorf("Add(%d, %d) returned %d, expected %d", tc.x, tc.y, result, tc.expected)
			}
		})
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

3.3 テスト範囲

使用go test -coverテストカバレッジを表示するには

go test -cover
PASS
        modu    coverage: 100.0% of statements
ok      modu    1.149s
  • 1
  • 2
  • 3
  • 4

4. Go 単体テスト ツールキット – testify

Go言語で単体テストを行う場合、公式のアサーション関数が組み込まれていないため、通常は大量のアサーション関数を使用する必要があります。if...else...テスト結果を確認するためのステートメント。ただし、次のようなサードパーティのライブラリを使用すると、testify/assertを使用すると、一般的に使用されるさまざまなアサーション関数を簡単に呼び出すことができます。これにより、テスト コードが簡素化されるだけでなく、問題を迅速に特定するのに役立つ明確でわかりやすいエラー説明情報が生成されます。

上の例では、if...else...テスト結果を検証するための声明

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			result := Add(tc.x, tc.y)
			if result != tc.expected {
				t.Errorf("Add(%d, %d) returned %d, expected %d", tc.x, tc.y, result, tc.expected)
			}
		})
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

今すぐ利用可能testify/assert上記の判定プロセスを簡略化すると以下のようになります。

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			result := Add(tc.x, tc.y)
			assert.Equal(t, result, tc.expected)
		})
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

testify/require持っているtestify/assertすべてのアサーション関数。それらの唯一の違いは次のとおりです。testify/require失敗したテスト ケースが発生した場合、テストは直ちに終了します。