branch4 Blog

Internet development with every single possibility.

First RSpec With Rails

| Comments

こんにちは。adorechicです。 まわりでRailsデビューした人が何人かいるのですが、 「テストどうすりゃいいんだ」「RSpecわからん」 といった歓喜の声があがっているため、はじめてそのへんを触る人がざっくり雰囲気がつかめるようなエントリを書いてみます。 RSpecのセットアップとかそういうのは書きません。

specの種類

specと一口に言ってもいろいろな種類があります。

  • controller spec
  • model spec
  • view spec
  • routing spec
  • helper spec
  • request spec
  • feature spec

まずこれどういう違いがあるのかーとかわからないですね。 モデルとかのテストは良いとしても、controller specとrequest specってどっちも「あるアクション」に「GETリクエスト」みたいなリクエストがとんで「どうなる」というのをテストしているように見えて、何が違うねんとかあると思います。

それぞれどういう違いがあるのかざっくり解説。 の前に。

テストって何をテストするのか

原則として、

  • テストの対象(主題)に対して
  • ある「入力」を与えたとき
  • どういった「出力」が得られるか

これが基本です。これが全てです。 といいたいところですがステートレスにはなりきれないところもあるわけで。 必要に応じて、

  • どういった「内部変化」があるか

も見ます。 ただしこの内部変化はいたずらにチェックすればよいわけではないです。 ありがちなのが、テスト対象の実装に依存したテストを書いてしまうこと。

例えば最終的に「このレコードが保存されること」が確認できればよいのに、別の「このメソッドが呼び出されていること」とかを無闇にテストしてしまったりします。 そういうテストは実装が変わったときに大量にfailして、テスト書き直すはめになるという壊れやすいテストになってしまいます。これでは本末転倒です。

あくまでテストは対象のインターフェイスに対して行います。

この辺の話に興味ある場合はこれとかオススメ。

ということで何が「入力」で何が「出力」なのかをおさえると、それぞれのspecの役割がとりやすいと思います。

specの種類

本題。

controller spec

対象はコントローラー、と特定のアクション。

勘違いしやすいところですが、「入力」はHTTPリクエストまんまではありません。 paramsなどを入力としてactionメソッドが呼び出されているととらえた方がやりやすいと思います。

同様に「出力」もHTTPレスポンスではありません。 よくあるのが、response bodyのhtmlとかをチェックしてしまうこと。 単体テストとして考えるとそれはviewの責務なので、controllerの責務ではないのです。

対象のactionに、あるパラメータがわたされて、その結果200が返る、404が返る、リダイレクトされる・・・とか、その処理であるレコードが作成されるとかそういうのを見ます。

model spec

モデルに対するspec。なんかデータ作って保存したり検索したりするようなメソッドをテストします。 いわゆるよくある単体テストっぽいのでわりとわかりやすいんですが、やりがちなのはrailsの機能までテストしてしまうこと。validate presenceのような、デフォルトで用意されているrailsのvalidatorの機能までテストする必要はないです。 たとえばcontextによって発動するvalidatorが違うとか、そういう複雑なことをするときだけ。

view spec

ビューをテストするもの。コントローラーの処理後、必要な変数がアサインされた前提で、どういったものがレンダリングされるか、という観点。

routing spec

railsのroutingをテストするもの。結構複雑なrouting書いたりすると、このURLできたときどのアクションにふられるのかとかぱっと見わからないときとかも便利。

helper spec

実は一番わかりやすいかもしれない。railsのhelperに対するテスト。helperって基本入力とか条件に対してタグだったり文字列を整形して出すようなユースケースが多いので、一番入力と出力がわかりやすい。

request spec

request specとfeature specは一応Integration testと呼ばれるものに属します。ただIntegration testってコンテキストによってちょっと意味合いが違ってくるのですが、ここではrailsアプリケーションとして通してるというぐらい。request specの入出力はHTTPリクエスト・レスポンス。REST APIに対するテストに向いている。一方でfeature specは画面操作。普通にブラウザでアクセスして、何かボタンをクリックするとどうなる、というのをテストする。

feature specでは、controller specなどと違いDBの状態がどうなった、とかリダイレクト先のURLがどうだというようなことはあまりみません。だってブラウザで操作してて見えないから。登録ボタンおしたら、画面の要素が増えたねーみたいにチェックしていきます。

どの種類のテストをどこまで書くのか

まずmodel specでいろいろテスト書いたんだけれども、それを呼び出すcontroller specでも同じようなテスト書いてくとすごい重複してないか、という疑問。 これも、入出力がどう違うのかという意識で書いていくとやりやすいと思います。

例えばmodelのメソッドは、そのときのレコードの状態や、わたされるパラメータによっていろいろなコンテキストがあって、それごとにテストを書いたとしましょう。 しかしその結果の出力としてのケースはそれほど多くないのではないでしょうか。成功してレコードが保存される、失敗してエラーが返るぐらい。 であれば結果として呼び出し側としては2ケースしかない。であればcontroller specではその2ケースのテストを書く、というイメージです。

specいろいろ種類あったけど、全部書くの?

全部書く必要はないです。必要なものだけ書けば良い。

これはそのアプリケーションで必要とする要件であったり、チームの開発スタイルとかにかなり依存してくるところなので一概にいえないところですが、 たとえば基本的なresourcesとかによるroutingしかないのであれば、頑張ってrouting spec書かなくてもよいと思います。

viewにすごいこだわりを持っていたり、複雑なviewを持っているアプリケーションであれば、view specしっかり書くとよいし、 そこまでではなく普通に操作できればよいやーぐらいであればfeature specがあればよさそう。

逆にそういう前提にこだわらず、不安なところは普段書かないやつでも書いた方がよいです。

よく書かれるのはmodel, controller, feature/request specあたりでしょうか。

まとめ

テストに正解はありません。

テストはそのアプリケーションが価値を生み出す手助けをするものです。 新しく追加したコードが既存の機能を壊していないことを確かめるために用いられることもあるし、開発者が開発を進める上での手助けをするために用いられることもあります。

どういった目的で、どんな場面でテストを書くのかによって書き方はもちろん、ツール自体使い分けた方がよい場合もあります。

また開発スタイルや対象とするアプリケーションの性質によっても異なります。ぶっちゃけ好みです。 RailsプロジェクトではRSpecが多いような感じがしますが、お膝元のBasecampではtest/unitらしい。

複雑な機能を作るときに、contextを分けながら考えを整理してから実装入るとやりやすかったりするし、 逆にインターフェイスレベルでいろいろ試行錯誤しながら開発するときは最初からきっちり書いてると開発効率を落としてしまったりします。

とはいえ慣れないうちはたくさん書くのが良いと思います。

Comments