概要
この記事では、RSpecにあまり馴染みがない人にもわかりやすいように、RSpecの理想的な書き方(=コーディングルール)を説明しようとしています。
書いてあることは個人的な見解です。理性的な議論を歓迎します。
友人に語っているような文体ですのでお気をつけください。
動機
俺はみんなにRSpecを書いてほしかったんじゃない。
いいRSpecを書いてほしかったんだ(´・ω・`)
なぜかバリデーションだけ一生懸命にテストされていて自作の30行近いメソッドにテストがないとか、10行近いbeforeブロックがコピペされまくってるとか、そういうのはさ、見たくないんだ。
あと、http://betterspecs.org/ は読もう。日本語版もあるし。そこに書いてあることはここでは繰り返さないので、あしからず。
総論
はじめに
ここから先は読まなくてもいいからこれだけは読んでほしい。
itブロックで日本語を使うのだけはやめようぜ。
ここで個人の思いのたけをぶつけるのはやめておくけど(「プログラマなんだから簡単な英語ぐらい使おうぜ!」とか)、
it "引数がないときエラー"
みたいなのはさ、RSpecの最大の特長であるテストコードとドキュメント出力の可読性を大きく落とすわけですよ。最大の特長を殺してしまったら、後にはいいものは残らないんだ。この特長は大事にしたいんだ、うん。
あと、これは個人的な経験なんだけど、**日本人メンバーが誰も理解できないような英語でなくては説明できないメソッドを書いているとしたら、何かが間違っている可能性が高い。**そういう意味でも英語を使うのはいいプラクティスだと思う。
テストファーストについて
悲しいお知らせ:何を書けばいいのかわからないのに、何をテストすればいいのかわかるはずがない。
というわけで、テストファーストに関してあんまり神経質になる必要はないと思う。仕様が明確でないなら、テストは後回しでいいよ。
何をテストするのか
「全部」と自信満々に答えたあなたは死ぬまでテストを書いていてほしい。
これは結構冗談ではない。というのも、忘れてはいけないこととして、僕らがテストを書くのはそれがユーザーに価値を届けるからだ、という事実がある。そしてそれは他の多くの手段との兼ね合いになる。だから、プロジェクトの初期からテストを完璧にするのはあまり推奨されていない。それは、その段階ではひとまず動作するコードを書くことの相対的な価値が非常に大きいから(同様の理由でコードの綺麗さにこだわり過ぎることもあまり良くないとされているみたい)。
で、テストの価値って何かって考えると、
- 開発者がバグを恐れずにコードを改善できる(それは後続の開発者の生産性向上につながる)
- バグをユーザーより先に発見できる確率が上がる(ユーザーがTwitterで文句を言わないのはいいことです!)
- テストを書いているうちにコードの設計のまずさに気づくかもしれない(それは技術的負債を回避することにつながる)
要は、限られた時間でテストの価値を最大化するにはこういったことに気をつけなくてはいけないということ。
上で挙がっているテストの価値に対応するような箇所というのは、
- 内部の処理は複雑だけどインプットとアウトプットは明確であるような箇所(テストが通っている限り、内部の変更は自由だ!)
- 多くのバグの原因となっていることがすでに明らかな箇所(リグレッションが減らせる)
- 自信がない箇所(テストが通っているなら少しは安心できるでしょ?)
となる。
もう一つ重要な話として、テストもコードであるという事実についても話しておかなくてはいけない。どういうことかというと、
モデルをちょっといじったらテストがめちゃコケるのは辛すぎる
ということ。テストコードはテスト対象コードに依存してるので、いつも依存しているのと同じように、原則的にはパブリックメソッドについてだけテストするべき。
プライベートメソッドについてテストを書いちゃいけないわけじゃないんだけど、「よし、このメソッドプライベートだから、がっつり書きなおすぜ!」→テスト落ちてましたorzみたいなことが起きると悲しいので。
あとはエッジケースについてのテストは価値が高い場合が多い。この辺はソフトウェアテスト全般の話だから、そっち系の本を読んでほしい。
テストの種類の話
※Railsの話です。
モデル、コントローラー、ビュー、リクエスト、フィーチャー、アクセプタンス…
多すぎる!
全部書くのは現実的でない場合が多いので(全部書けるチームならぜひ雇ってください)、書くのを絞らなくてはならない。
経験上、書いたほうがいいテストの種類というのは
- サーバーサイドレンダリングアプリ → フィーチャー
- JSON APIアプリ → リクエスト
- 共通 → モデル
って感じ。それ以外の種類が無駄かというとそうではないけど、価値と時間の問題やら、依存性の問題やらを考えたときに、それ以外の種類はあまりコスパがよろしくないので。
カバレッジの話
カバレッジは忘れろ!
って言えたら嬉しいんだけど、まあ実際、低すぎるのは問題あるので。
まず、simplecov
とかで50%とか出たらやっぱり反省したほうがいいんじゃないか。それこそフィーチャースペックやらリクエストスペックやら書いてたらありえない数字だし。
一つハッキリ言えるのは、100%近くても安心するなということ。例えば「外部のコード(フレームワークのコードや外部サービスのクライアントライブラリなど)の適切なメソッドを適切な引数で呼べているか」みたいなことはカバレッジの数字には出ない。そして、僕らのアプリケーションの(Webアプリなら)大部分は外部のコードなので(自作フレームワーク?それはまた結構なことで…)、カバレッジの数字は実際のカバレッジとは程遠いということになってしまう。
各論
文法
-
before :all
はできるだけ使わない - インスタンス変数はできるだけ使わない
-
subject
はcontext単位で共有されているなら使う(テストケースごとに違うなら使わない)
モデル
- 自作のパブリックメソッドは完璧にテストする(完璧というのは、正常に動作する引数の組み合わせ全部、ぐらいの意味)
- アソシエーションは
dependent
オプションがある場合は作成・削除についてテストすることを推奨、ない場合は任意 - scopeは取得できるデータを1件、できないデータを1件用意してからテストすることを推奨(ただし、
order
が重要なケースではその限りではない)
フィーチャー
- JSの処理が多すぎる場合は後回し
- ログイン処理はクッキーを直接セットしても良いものとする
- CRUDは一つのテストケースでまとめてテストしても良いものとする
リクエスト
- GETのリクエストをテストする場合、可能な限り、レスポンスのJSON全体がマッチするようなテストを書く
- GET以外のリクエストをテストする場合、リクエスト後にモデルのファインダーを直接呼び出してテストしても良いものとする
- ステータスコードは毎回テストする
まとめ
ホントはもっと書くことあったんだけど力尽きたぜ…
ちょくちょく加筆する予定です。