はじめに
ひとつ前のエントリでRSpecの話を書いたので、それにちなんで最近僕の身の回りで起きたテストコードに関する雑多なエピソードをいくつか書いてみます。
その1:テストコードを書いてない処理で見事にバグを出してしまった・・・!!
僕はソニックガーデンの中では「テスト番長」の異名を持っていて、基本的にテストコードはしっかり書くタイプです。
ですが、どうしてもリリースを優先しなければいけないときは、やむを得ずテストコードを後回しにして先にリリースすることもあります。
先日リリースした「テストを後回しにしたコード」は、リリース直後は問題なく動いていました。
その数週間後、別の仕様変更が入り、変更したコードをリリースしました。
「テストは全部パスしているので大丈夫なはず~!」と思ったら、リリースの翌日に変なシステムエラーが発生。
エラーが起きている場所を見て、ガーン。
例の「テストを後回しにしたコード」でしっかりエラーが起きていました。
正常系のフィーチャスペックを書いておくだけで、確実に検知できたエラーです。
やっぱりテストはちゃんと書いておかないとダメだな、とセルフ反面教師になりました。
その2:JavaScriptのテストも書いておかないとRailsのアップグレードはしんどい
結構昔に作った社内向けのツールを先日Rails 5にアップグレードしました。
基本的にテストはしっかり書いていたのですが、ソニックガーデンに入社して間もない頃に作ったRailsアプリで、当時不慣れだったJavaScriptを実行するフィーチャスペック("js: true"を付けるスペック)はひとつも書いていませんでした。
そのため、JSを使った画面の動きはいちいち手作業で確認せねばならず、非常に手間がかかりました。
ちゃんと"js: true"のフィーチャスペックも書いておかなければいけませんね。
ちなみに、テストを全く書いていないRailsアプリだったらもっと悲惨なことになるのは言うまでもありません。
その3:すぐに終わるテストはRailsのアップグレードがラク
これまた先ほどのRails 5アップグレードのエピソードです。
社内向けツールは小規模なRailsアプリでテスト項目が少なく、全部のテストを実行しても20秒以内に終わります。
なので、アップグレードの作業中は頻繁にテストを再実行して、エラーや警告を潰していくことができました。
普段の開発ではここまでテストを再実行することはありませんが、「速いテストのありがたみ」をしみじみと感じました。
昔から開発し続けている大きなRailsアプリだと15分とか20分ぐらいかかるものもあるので、気軽に全部を再実行するのは厳しそうです。
こういう場合はRSpecのフィルタリング機能を使ったりして、頻繁に再実行するのは特定のspecだけに絞り込んだ方がいいかもしれません。
その4:テスト番長は社内でRSpecの質問に何でも答えてくれます
RSpecは僕の得意分野なので、社内で他のメンバーがテストの書き方で困っていたら気軽に質問や相談に答えるようにしています。
「伊藤さん、ここのテストコードがうまく動かないんだけど、一緒に見てくれる?」
「テストコードの共通化で悩んでるから相談に乗って~」
といった質問や相談があれば、だいたい数分で「はい、論破」・・・じゃなくて「はい、解決!」しています。
RSpecって、それなりに動きにクセがあるので、ハマってしまうときはとことんハマってしまうんですよね~。
まあ、僕も昔はよくハマってたんですが、かなりの経験値を積んだので、「こういうときはこう書く」「こういうエラーが出たら、たぶんこれが原因」というテンプレートがだいたい頭の中にできています。
Twitterとかを見ていると「RSpecがうまく書けない!思った通りに動かない!キィ~~ッ!!!」という悲鳴をよく見かけるので、「あ~、僕が見たらすぐに直るんだろうなあ。お手伝いしてあげたいな~」と思ったりします。
RSpec相談コンサルとかやったらいいお小遣い稼ぎになるかも?
1回500円でどうですか??(笑)
その5:雑に書けるときは雑に書くのが最近の趣向
これはRSpecの書き方の話です。
RSpecって結構「RSpecらしく書くこと」を求められたりします。
たとえば、describeやcontextでしっかりグループを分けましょう、再利用するデータはletやsubjectに切り出しましょう、ひとつのexample(it)の中でテストするのはひとつの項目だけにしましょう、等々の方針です。
典型的な例がBetter Specsを読め、Better Specsに書いてあるとおりにspecを書け!っていうパターンですね。
これはもちろんそれぞれに意味があってのことですし、かつては僕もそうあるべきだと思っていました。
しかし、最近は「そこまでがんばってキレイにしなくてもいいのでは?」と考えるようになってきています。
その結果、テストコードがだんだん雑になってきています。
具体例を挙げるとこんな感じです。
まず、RSpecらしく書いたバージョンはこれです。
describe Cloth do describe '#price_with_tax' do let(:cloth) { Cloth.new('RSpec Tシャツ', price) } subject { cloth.half_price } context '割り切れる場合' do let(:price) { 1000 } it { is_expected.to eq 500 } end context '割り切れる場合・その2' do let(:price) { 2000 } it { is_expected.to eq 1000 } end context '端数が出る場合' do let(:price) { 999 } it { is_expected.to eq 499 } end end end
次に、雑に書いたバージョンがこちらです。
describe Cloth do describe '#half_price' do example do # 割り切れる場合 cloth = Cloth.new('RSpec Tシャツ', 1000) expect(cloth.half_price).to eq 500 # 割り切れる場合 cloth.price = 2000 expect(cloth.half_price).to eq 1000 # 端数が出る場合 cloth.price = 999 expect(cloth.half_price).to eq 499 end end end
ポイントとしては、
- describeやcontextのグループ分けは必要最小限にする
- contextに相当するものはコメントで書く
- ひとつのexampleの中で、まとめて複数のテストを書いてしまう
- letやsubjectに切り出さず、ローカル変数にしたり、毎回同じようなコードをベタ書きしたりする
- "it 'returns half price'"とか"it '半額の値を返す'"のような説明文を省略して、"example"だけで済ませる
みたいな感じです。
メリット・デメリットはいろいろありますが、メリットだけを挙げると、
- 思いついたテストパターンを上から下へさくっと書ける
- どれをletやsubjectにすべきか、どうcontextを分割すべきか、といった「テストコードの設計」に頭を使わなくて済む
- 「意図した通りにコードが動いていることを検証できる」という点では、RSpecらしいテストコードと変わりがない
みたいになるかな、と思います。
あと、RSpecらしいコードを追求して「RSpec、かくあるべき」を全面に出してしまうと、初心者の人たちに「RSpec怖い」「RSpec難しい」「RSpecわけわからん」という恐怖心を植え付けてしまわないかな~という懸念も感じたりします。
もちろん、RSpecらしいコードを完全に放棄したわけではありません。
テストコードの内容によってはRSpecらしく書いた方が、読み書きがしやすかったり、保守性が高かったりするケースもあります。
そういう場合はRSpecらしいコードを書くようにしています。
「よくわかんないけど、これが巷で言う守破離の「離」ってやつ?」なんて自分では考えたりしています。(間違ってたらすいません)
2016.9.5 追記
Qiitaに「雑なRSpec」の記事をもう少し詳しく書きました。
よかったらこちらもどうぞ。
まとめ
というわけで、テストコードに関する最近の雑多なエピソードを書いてみました。
まとめると、「テストコードって大事!テストコードって楽しい!」ということですね。
・・・えっ、全然違う?
いや、「テストコードって大事!テストコードって楽しい!」っていう思いは昔から変わってないので、そういうことにしておいてください。
ではではこのへんで!
PR:RSpecの電子書籍を販売しています
僕が翻訳した「Everyday Rails - RSpecによるRailsテスト入門」という技術書を電子書籍で販売しています。
RSpec初心者の方でも十分わかりやすい内容になっているので、まだ読んでない方はぜひ読んでみてください!