noerrorgithub.comgo用のテストライブラリを作りました(言いたいことは分かります)。使いかたはこんな感じです。noerror.NotEqual(30).Actual(add(10,20)))以下詳細。motivation元気があるときにはtestingパッケージをそのまま使うのが良いという事は分かるし、その方が見通しも良くなることもあるということも分かっています。元気が無いときやまたは「我gopherぞ?」みたいな自尊心に溢れていない時にはついつい面倒になってしまうんですよね。ifは許容範囲なんですがSprintf形式の文字列を書くのがめんどくさい感じです。個人的には、特にリリース対象ではないコードや日常的な調査用のメモのコードでも結果を確かめるためにテストコードを書く事があるんですが、そういう時に面倒に感じたりしてます(テストコードが必要な理由は頭があまり良くないせいかもですね。いうことにしておきましょう)。そんなときに今までtestifyを使っていたんすが、これは欲しいことに対して機能が多すぎるかなーと思ったりしてました。github.comtestifyは色々比較用の関数(matcher)がたくさんあるんですが覚えきれないし結局使うのは一部だけ。加えてほかの環境でも常に利用したいかと想うかというとそうでもない。網羅して覚えようというモチベーションも少なめでした。そんなわけでちょっとしたassertionライブラリが欲しかったんです。complicatedイメージ的にはそんな感じです。conceptsせっかく作るんでもうちょっとまじめにコンセプトを考えてみようかなと思いました。考えてみると、実質エラーチェックだけできれば十分で、細かな記述なしにそれなりの形でエラーメッセージをつくってくれれば十分かもと思ったりしました。そんなわけで、以下がコンセプト。dependenciesエラーチェックだけできれば十分なのではというのは仮説です。(依存がゼロというのは趣味みたいなもので省略します)ついでにtestifyを使っていてちょっと嫌だなーと思う部分にも対応することにしました。作っているときに考えていたことここからは作っている時に考えていたことを詳しく。libraryテスト用のライブラリで必要なのはエラーチェックだけで十分なのでは?というのはこういうことです。通常testingパッケージを直接使って手書きしたようなコードでは以下の様なコードになると思います。got)}if文はともかくErrorfの方がめんどくさいわけですね。testifyを使ったコードはこうなります。add(10,20))これは短いのですが、個別にmatcherが定義されていて、これを適宜使い分けたり存在を認識するのがめんどくさかったりします。元々の目的が日常的な書き捨て用途のものだったりするので(記憶が確かならYAMLなどにも依存するのでgo.modを汚す感じなのも気分があんまり良くなかったりします)。今回作ったライブラリを使ったコードではこうなります。noerror.Equal(30).Actual(add(10,20)))testifyより長くはなります。エラーチェックそしてassertionは結局エラーチェックだけで良いということの意味なのですが、先程のコード(再掲)のEqual()は問題があった時にエラーを返すような関数です。Should()はエラーが渡されたときだけ反応する関数です。noerror.Equal(30).Actual(add(10,20)))これらをそれぞれassertion関数、comparison関数と呼んでいます。一般的には以下の様な形で利用されます。<Comparison>(want).Actual(got))つまりassertion関数はエラーを受け取ってよしなにするだけ、そしてcomparison関数は何かを比較してエラーを返すだけの関数です。実際定義されているassertion関数は以下の2つだけです。Must()Should()Must()はtestifyのrequire.NoError()に対応し、エラーを受け取ったらt.Fatal()を呼び出します。Should()はtestifyのassert.NoError()に対応し、エラーを受け取ったらt.Error()を呼び出します。これでおしまいです(実際にはログ用のLog()も定義されていたりします)。エラーが発生した場合は以下の様なエラーメッセージになります。2ちなみにエラーメッセージのフォーマット自体はあまり真剣に考えたものではないのでご意見ご要望をお待ちしてます。おもしろいのはエラーを返す関数のテストは以下のようにも書けるところです。doSomething()はエラーを返すような関数です。noerror.Must(doSomething())以下のようなメッセージがテストに失敗したときに出力されます(pkg/errorsによるスタックトレースの表示にも一応対応しています)。*ERROR*このコードの意味が分かるようにすることとEqual()などのcomparison関数の名前との対応を考えたときのパッケージ名でけっこう頭を悩ませたりしました。noerrorがmustだとなんとなく意味は通じそうな感じです。(実は最初はeasyとsimpleのちょうど良い塩梅ということを現したくてhandyという名前でした。err)だと意味がわからないですよね)comparison関数comparison関数は以下の6つです。意味は関数名からなんとなく解ると思います。JSONEqualだけ特別かもです。それぞれ=でstrictに比較するもの、reflect.DeepEqual()でゆるく比較するもの、JSON形式に変換してから再変換をかけてよりゆるく比較するものの3種類です。Equal()NotEqual()DeepEqual()NotDeepEqual()JSONEqual()NotJSONEqual()これだけなのでわりと見通しが良いです。Nil()みたいなものを定義していこうとするとやがてtestifyになってしまうということでこれ以上増やすのは止めました(floatのような微小な差分が出てしまいそうなものやunorderedなslicesなどは頑張ってください)。Actual(<value>)ここからはtestifyで気になったことについても。testifyを使った時に気になった部分のひとつ目は比較対象の値がどちらの目的で使われているのかわかりにくい点です。いうことが分かるのですが、そういうことを考えたくない気持ちがありました。valueを指定するようにしました(testingパッケージ直打ちの場合には変数名とErrorfに渡すメッセージの部分でどちらか分かるようにしてますよね。ちなみに実装の途中でActual()代わりにExpected()を利用するようにすると後で面倒な話になりそうと言うことが分かったりしました(これは後で説明します)。Describe()エラーメッセージのときにちょっとだけ文脈を追加したいですよね。エラーメッセージが以下だけだと分かりづらい。2Describe()を使うとメッセージの先頭部分を変えられます。1"))結果はこう変わります。argumentsもうちょっと情報を付加したいことがあると思います。APIのテストを書いている時にstatusが200を期待するテストで間違ったrequestになってしまったときなど。修正後のテストコードでは問題にならない部分なのですが、テストの修正中にはエラーレスポンスのbody自体が確認できると便利だったりします。Requestのときのエラーレスポンスにエラーの原因が書かれていたりすることがあるので(printデバッグをたまにしちゃったりもします)。そんなわけでもう少し情報を余分に付加したくなります。これはtestifyに揃えてShould()などが余分に引数を取れるようにしました。":bomb:")は以下の様な表示になります。10:bomb:実はここの部分と先程のエラーレスポンスの中身を表示したいという要求の相性が良くなかったりします。これはtestifyにも関連することなんですが、testingの場合にはif文で分岐されるので一度消費済みのio.ReadCloserが渡されることはないんですが(もちろん無いようにテストコードを書いてますよね?)、ここをeagerに評価してしまうと予期せぬ消費が行われて後のコードでエラーになるみたいなことが発生します。似たような機能を作るときにはlazyにしましょう。今回の実装ではfmt.Stringerを実装したものならそちらを使う様になってます。以下の様なstructがたまに役に立つかもです。optionsのような形で提供していたんですけれど。邪魔に感じたんですよね。Describe()も含めて。そんなわけでDescribe()はメソッドにこちらは可変長引数にして対応することにしました。ボーナスポイントここでボーナスポイントです。thingと言うやつです。戻り値として多値でエラーを含んで返す関数があったりしますがこういう関数へのassertionがtestifyでは厄介でした。これを一行で書けるようになってます。c)1行で書ける様になってます。ActualWithError()です。noerror.Equal(0).ActualWithError(Count()))実際にはちょっとだけ特殊なことをしていて、Must()を使っていようがShould()を使っていようが多値を返す関数でエラーがかえって来たらt.Fatal()が呼ばれます。途中で実行をストップします。場合、この種のコードがテストコード上に現れるのは、テスト対象の実行後のDBの整合性チェックだったりするので。ちなみにActual()のかわりにExpected()を定義する様に実装していくとcomparison関数の種類が膨れ上がります。ツライですね(伏線の回収)。さいごに今回はREADMEなどをけっこうまじめに書いたのですが英語があまり得意ではないのでPRなどお待ちしております。wayとはーで説教するされるという感じではなく)。19:08go用のテストライブラリを作りました(言いたいことは分かります)Tweet広告を非表示にする関連記事2019-01-07ertを使ってelispのunittestを書く方法のメモertを使ってelispのunittestを書く方法のメモ。structの生成にfactory的な関数を用意しておくと埋め...2018-01-22goでのslack通知(mail通知)のコードについて考えたりしていたgoでのslack通知(mail通知)のコードについて考えたりしていた。componentという機能を追加した月別アーカイブはてなブログをはじめよう!podhmoさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。