2017-07-02なんか気軽にメソッド追加したら、\\わけわかんないテストでこけて、\\今日はテスト通すのに1日かかりました、上長。僕はエンジニア辞めたくなります。「なんでこんな書き方したの?」の大海原Rspec。人が理解しやすくなるようにrspecを書きたい人向けの記事でございますdescribe,context,exampleはなぜ書くか?describe,context,exampleはなくても、テストデータを生成しexceptに期待する動作を書くことでテストは行えます。describe,context,exampleが無かったり間違った使い方をすると以下のようなことが起きます。・テストを追加するときに無駄なデータ生成や宣言を行いやすくなる。・テスト内容が理解しづらくなる。正しい使い方を心がけましょう。今回は、正しくテストはできているがわかりづらい書き方をしているテストをリファクタするような流れで説明していきます。段階的にリファクタしていくので、部分的に見るとベストプラクティスではないので、ご注意ください!ただ、どれもより良く改善をするように書くのを心がけました。今回は、並び替え(order_by_displayorder_and_updatedat)を例に書いていきます。この並び替えメソッドの処理は、0更新日順の並び替えよりも、表示順の並び替えを優先する。1表示順(display_order)を昇順に。2更新日順(updated_at)を降順に。いう並び替えを行います。以下のような"元データ"を差し込んだ場合、"並び替え後のデータ"は以下のようになります。分かりにくくなりやすいテストが以下です。生成順でupdated_atが変わるテイです。4]end一見、問題ないようにも思えます。事実、categoryテストがこれからも少ないなら問題ないかもしれません。このテストには以下のような問題があります。このrspecから、テストデータだけではテスト内容を理解しづらい。category.rbに定義されている"order_by_displayorder_and_updatedat"の処理を読みに行く必要が出てくる。「display_order」「updated_at」以外の要素も考慮しなくてはいけないメソッドになった場合、その分だけテストデータが増え、並び替え処理で何をしているかわかりにくくなる。Categoryモデルの他のメソッドもテストする時に、テストデータやexceptが何を対象としているのか分かりにくくなる。本来、Category.order_by_displayorder_and_updatedatのテストをしたいだけなのに、テストデータの生成のコードが多く、処理が埋もれてしまっている。テスト項目が増えたときに同じ内容のcreate文が重複するので、改変が必要になったときに変更箇所が多くなる。これらをまとめて言うと、テスト項目が増えてくるとテスト内容がより理解しづらくなります。明日からプロジェクトに参加する人にも分かるように書くつもりで、リファクタしていきましょう。リファクタ1:テスト内容を伝える「example」まずは何のテストをしているのか説明してあげましょう。endendexampleを書いたおかげで、spec内だけで、order_by_displayorder_and_updatedatおおよそ理解できるようになりました。order_defaultのようなメソッド名だと、名前だけで処理を把握するのは困難になるので、説明してあげましょう。"order_by_displayorder_and_updatedatが正しく並び替えする"というような説明を過去自分は書いていましたが、これはやめましょう。処理することは当たり前なので、どのような処理をしているのかを説明してあげましょう。現在の例では、5つのテストデータを並び替えています。「display_orderの並び替えが昇順で行われているか」という問題と「updated_atの並び替えが降順で行われているか」という問題と「updated_atよりもdisplay_orderを優先できているか」という問題が関わっています。このような3つの問題を一つのexampleに収めていると、example内で並び替えの条件が見えづらい問題があります。これらの問題をcontextで場合分けしてテストを行います。endendこのように場合分けすることで、それぞれのexampleに関わっているテストデータも減り、それぞれのexample内で何をチェックしたいのかが明示的になります。リファクタ3:テストの対象を説明する「describe」リファクタ2で3つのcontextを作成しましたが、これらはすべてorder_by_displayorder_and_updatedatメソッドに関するテストです。この状態で、他のメソッドのテストを追加したときに、そのcontext,exampleが何を対象としたテストなのかが分かりづらくなってしまいます。この3つのcontextをdescribeでまとめてあげましょう。endend3つcontextをdescribeで囲んであげただけですが、これで他のメソッド(#other_method)のようなものを追加しても、テストの対象が明示的になります。テストデータを共通化し、example内でのテストデータ生成を控える。リファクタ1〜3でdescribe,context,exampleを作成した後は、テストデータの共通化をしていきましょう。テスト項目が少ない時は共通化のメリットはあまりありませんが、example内ではテストしたい処理だけが書かれているようにすることで、このexampleで何をテストしたいのかが明確になります。endendまた、テストデータを共通化しているときは、それぞれのexampleでテストデータの内容を更新するのは控えましょう。あるdesribe,context内で共通化しているテストデータを変更すれば、その以下にある、すべてのexampleは影響を受ける関係がベストです。リファクタリングのビフォーアフターリファクタリングの前後は以下のようになりました。」ぱっと見そう思いますね。テストコードは基本的に、「こけてから出会うもの」です。処理において、「updated_atを降順に並び替えできなくする」コードを書いてしまったとします。"updated_atを降順に並び替える"でこけるので、「updated_atを降順に並び替える処理が違っている」ことがわかります。🏹まとめrspecをより良くする方法は無限大です。今回のメソッドの例だと、過保護なテストだったかもしれません。どれだけ充実させるかは決めましょう。最低限の配慮としてdescribe,context,exampleの基本的な使い方は正しくしましょう。(エラーが起きてくれないので気をつけましょう。)あなたのテストで、誰か、もしくは数週間後の自分が足止めを食らってしまいますいつかの誰かに定時という名のプレゼントをrspecでした。