2023/05/15

Goのアサーションライブラリ actually のすべて

ここ 1ヶ月くらい書いている Go のアサーションライブラリ actually ですが、ぼちぼち pull-request を重ねて、そろそろ v1.0.0 も見えてきたのではないかということで Release を v0.13.0 で作りました。

いまのところ、圧倒的な 俺の俺による俺のためのテスティングライブラリと化しています が、そろそろ他の人にも使ってみてもらいたい気持ちが湧いてきています。というわけで、ここでいったん現状のメソッドすべてに解説を書いてみようと思います。

actuallyの概要

インターフェースは以下の様な感じです。

actually.Got(v).True(t)
actually.Got(v).Expect(vv).Same(t)

1値をアサーションする場合も、Got() でテストする値をセットして、各アサーションメソッドを *testing.T を引数にして呼び出します。 2値をアサーションする場合は、Got() と Expect() で値をセットします。Got() と Expect() はどっちを先に書いても大丈夫です。また、2値アサーションメソッドは、すべて Same という接頭辞ではじまります。インターフェースは、読むとき、書くとき、同じように一貫して明示的であることを重要視しています。

テストが こけたときのフェイルレポート は以下の様な感じです。

=== RUN   Test
    prog_test.go:10: 
            Trace:          /tmp/sandbox1598591906/prog_test.go:10
            Function:       Test()
            Fail reason:    Not same value
            Expected:       Type: int, Dump: 2
            Actually got:   Type: int, Dump: 1
--- FAIL: Test (0.00s)
FAIL

開発中、テストはさまざまな理由でこけます。なぜこけたのか、どうあるべきなのか、受け取った値はどんな型で具体的に中身は何だったのか、フェイルレポートが理解しやすいことはとても重要です。actually のフェイルレポートのフォーマットは testify をコピーしていますが、actually では「なぜこけたのか」という具体的な理由がより細かく表示されるようになっています。

というわけで、アサーションメソッドをひとつひとつ、全部紹介していきます!


1値アサーション

まずは、ひとつの値をアサーションするメソッドたちを紹介します。

True

actually.Got(v).True(t)

v が boolean型の true であればパスします。boolean型でなかったり、true でなければこけます。

False

actually.Got(v).False(t)

v が boolean型の false であればパスします。boolean型でなかったり、false でなければこけます。

Nil

actually.Got(v).Nil(t)

v が nil であればパスします。

NotNil

actually.Got(v).NotNil(t)

v が nil 以外であればパスします。

NoError

actually.Got(err).NoError(t)

err が nil であればパスします。

NoError メソッドのための、特別な Got が存在します。

actually.GotError(err).NoError(t)

GotError は Got と同じ actual value のセッターですが、error型以外を受け付けません。より厳密に扱えます。


2値アサーション

つづいて、2値アサーションです。2値アサーションメソッドは、すべて Same という接頭辞ではじまります。

Same

actually.Got(v).Expect(vv).Same(t)

v と vv が同じ値であればパスします。型が違うとこけます。値が同じであれば、ポインタアドレスは違っていてもパスします。

2値が同一であるかをみるには、ほとんどがこの Same を使うことになると思います。testifyで言うところの Equal です(厳密には Exactly と同じです)。

SameNumber

actually.Got(v).Expect(vv).SameNumber(t)

型が違っても、v と vv が convertable で同じ値とみなすことができればパスします。int32 と int64 や、int と float などの型違いの2値を比較したいとき用です。

SamePointer

actually.Got(ptr1).Expect(ptr2).SamePointer(t)

ptr1 と ptr2 が同じアドレスを指していればパスします。

SameType

actually.Got(v).Expect(vv).SameType(t)

v の型が vv の型と一致していればパスします。値は関係ありません。型の一致だけを見ます。


便利メソッド

actually には開発とテストを助けるいくつかのメソッドが準備されています。

Name

actually.Got(v).Name("v is true").True(t)

テストに名前をつけることができます。

この Name は、明示的なメソッドとして用意されていますが、実際はアサーションメソッドの第二引数にテストの名前を書くことができます。

actually.Got(v).True(t, "v is true")

X

actually.Got(v).X().True(t)

X はテストがこけたときに値を生データとして出力します。複数のタブや改行を含む込み入った文字列のテストのデバッグに役立ちます。

Diff

fmt.Println(Diff(a, b))

Diff は、2つのオブジェクトの差分を diff で返します。例えばフェイルレポートでは Got と Expect の差分は表示されますが、開発中はもっと手前の Got のもとになる値と Got の値の差分を見たかったりもします。そうしたとき、両方の値をダンプして目で差分をみるよりも、機械的に Diff で差分を出力することができると便利です。もちろん、文字列以外に構造体でも diff が見れます。

testing のラッパー

testing モジュールの機能をラップしたメソッドもあります。

FailNow

actually.Got(v).FailNow().True(t)

FailNow を呼ぶと、そのテストがこけると、そこでテスト全体を終了させます。有効範囲はそのテストのみです。

FailNowOn

actually.FailNowOn(t)

FailNowOn(t) を呼ぶと、そのテストファンクションの間、FailNow が有効になります。

FaiNotNow / FailNotNowOn

FaiNotNow / FailNotNowOn は、それぞれ FailNow と FailNowOn の逆で、テストがこけても後続のテストを実行するスイッチです。デフォルトの挙動です。FailNotNow は呼んだテストでのみ有効で、FailNotNowOn はテストファンクションの間ずっと有効です(テストがこけても後続のテストを続けて実行するのはデフォルトの挙動なので、特に指定がなければ(FailNowOn が呼ばれていなければ、明示的に呼ばれなくても FailNotNow が有効です))。

なお、個別のテストで呼ぶフラグの方が、ファンクション全体のスイッチよりも優先されます。つまり、FailNowOn が呼ばれたあとに、個別のテストで FailNotNow が呼ばれると、そのテストでは FailNotNow となり、こけても後続のテストが実行されます。

コードで見る方がわかりやすいかもしれません。

func Test(t *testing.T) {
    actually.FailNowOn(t)
    actually.Got(something).Nil(t)                    // Fail Now
    actually.Got(something).Expect(something).Same(t) // Fail Now

    actually.FailNotNowOn(t)
    actually.Got(something).Nil(t)                    // NOT Fail Now
    actually.Got(something).Expect(something).Same(t) // NOT Fail Now

    actually.Got(something).FailNow().Nil(t)          // Fail Now
}

Skip

actually.Skip(t)

Skip は単純に テスト実行時の -short オプションを見てテストをスキップするためのスイッチのショートカットです。


以上です。

ちなみに、errorのアサーションメソッドは今のところ NoError だけで、肝心の error そのものをアサーションするメソッドは実装していません。イケてる感じのを思いついたらやろうと思っているのですが、なかなか思いつかないので保留してます。だれか error のアサーションに一過言あるひといたらこっそり教えて欲しいです。

ではでは。

サイト内検索