sagantaf

IT関連の技術記事を書くブログ。

Go言語のtesting.Tとtesting.Mとtesting.Bとtesting.Fの違いと使い方

Go言語のtestingパッケージには、ユニットテストベンチマークテスト、Fuzzingテストを行うためのツールが組み込まれています。

このパッケージには、いくつかの重要な型が定義されています。

testing.T

testing.Tは、ユニットテストを行うための主要な型で、ユニットテストを行うためのメソッドが用意されています。エラーや失敗を報告するためのメソッド、サブテストを作成するためのメソッドなどがあります。

package main

import (
    "testing"
)

func Add(a int, b int) int {
    return a + b
}

func TestAdd(t *testing.T) {
    result := Add(2, 3)
    if result != 5 {
        t.Errorf("add(2, 3) = %d; want 5", result)
    }
}

上記コードをThe Go Playgroundで実行したい場合は https://go.dev/play/p/i5kAxJY4wt4

testing.M

testing.Mは、テスト前のセットアップやテスト後のクリーンアップを行うための型です。TestMain関数を定義することで、全てのテストの前後に実行したい処理を定義できます。この関数内でm.Run()を呼び出すことで、全てのテストが実行されます。

func TestMain(m *testing.M) {
        // セットアップ処理を行います
        setup()

        // 全てのテストを実行します
        code := m.Run()

        // テストが完了したらクリーンアップ処理を行います
        cleanup()

        // テストの結果に基づいて終了ステータスを返します
        os.Exit(code)
}

testing.B

testing.Bは、ベンチマークを行うための型です。ベンチマークは処理にかかる時間を計測します。testing.BNというフィールドを持っており、このフィールドはベンチマークを実行する回数を示します。

func BenchmarkAdd(b *testing.B) {
        // ベンチマークをb.N回実行します
        // b.Nの値はtestingライブラリで自動的に決定されます
        for i := 0; i < b.N; i++ {
            add(2, 3)
        }
}

testing.PB

testing.PBは、ベンチマークを並列で実行するためのものです。for pb.Next(){} のfor文の中に並列実行させたい処理を書きます。

func main() {
    testing.Benchmark(func(b *testing.B) {
        b.RunParallel(func(pb *testing.PB) {
            for pb.Next() {
                DoAnyThing()
            }
        })
    })
}

testing.TB

testing.TBは、testing.Ttesting.Bが共通に持つメソッドを持っている型です。テストとベンチマークの両方で再利用できます。

func assertCorrectSum(tb testing.TB, got, want int) {
    tb.Helper()
    if got != want {
        tb.Errorf("got %d; want %d", got, want)
    }
}

func TestAdd(t *testing.T) {
    assertCorrectSum(t, add(2, 3), 5)
}

func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        assertCorrectSum(b, add(2, 3), 5)
    }
}

testing.F

testing.Fは、Go 1.18から追加された、Fuzzing(ランダムなデータを使ったテスト)を行うための型です。ランダムな入力に対する処理をテストすることができます。下記の記事にわかりやすく書かれています。

future-architect.github.io

func FuzzParseMyInput(f *testing.F) {
        // 実行したいインプットをAddで追加します
        // Addした文字列以外のテストはランダムなデータが自動的に使われます
        f.Add("example input")

        // f.Fuzz内に、ランダムな入力に対するテストの挙動を記述します
        f.Fuzz(func(t *testing.T, s string) {
            // テスト対象の関数を実行します
            _, err := ParseMyInput(s)

            // 何らかのエラーが発生した場合
            if err != nil {
                t.Fatal(err)
            }
        })
}

最後に

これ以上の詳細は、公式ドキュメントが参考になります。

testing package - testing - Go Packages