こんにちは、hachi8833です。
今さらですが、RSpecをやる前に知っておきたかったことを記事にしました。少しさかのぼって、ソフトウェアのテストについても最初に簡単にまとめてみました。
ソフトウェアのテストとは
まず、広い意味でのソフトウェアテストは「人間による目視チェック」のようなものも含まれる点にご注意ください。
ソフトウェアの動作確認をどのように行うかは、ソフトウェア開発において常に大きな課題です。
当然ながら、ソフトウェアの動作確認を思い付きでやっていると漏れが発生しやすく、確認のたびにチェック内容が変わってしまったりするので好ましくありません。
そこで、ソフトウェアのテスト方法を体系化し、
- 誰がやっても同じようにテストできる方法
- 誰でもテスト計画を策定できる手法
が模索されてきました。
個別のテスト実施はドラゴン桜の名言でいうところの「作業」であり、ひとつひとつはそれほど重たいタスクではありませんが、テスト項目の漏れがないようにし、再現可能・繰り返し可能・手分け可能にすることが重要です。
Wikipedi: ソフトウェアテストを見ると項目が多くてびっくりしますが、これはソフトウェアテストが長年研究の対象となっており、大型機などの大規模な開発や学術研究でのテストをも網羅できるようになっているためです。
テストの自動化
ソフトウェアテストの研究が進み、大規模なソフトウェアが開発されるようになるにつれ、繰り返し行われるテストそのものをソフトウェアで記述して自動化する、という発想が生まれました。これは自然な考えです。
こうしたいきさつから、ソフトウェア開発ではある時期から「テスト自体をソフトウェアで行う」ということがさかんに行われるようになり、現在では言語や規模、案件を問わず開発手順に組み込まれるのが普通になっています。
個別のテスト支援ツールや、テストの自動実行ツールなどはたくさんありますが、そのほとんどは上で紹介したWikipediaのソフトウェアテストの項目を踏まえています。
なお、人間による目視チェックもPhantomJSなどによる自動化である程度カバーできるようになりつつありますが、完全に置き換えるまでにはいたっていません。
テストをどこまでやるか
初めのうちはこの点に一番悩むと思います。
テストの範囲は案件によって大きく異なりますので、「必ずこうしなければならない」と一般化するのは簡単ではありません。
不必要なテストは時間とコストの浪費につながりますので、お客様からのリクエストを満たすことを前提に項目を絞り込むのが普通です。
そのためにも、ソフトウェアテストにはどのような項目があるのかを一度は専門書で勉強しておくのがよいでしょう。
Railsのテストフレームワークによるアプリケーションの自動テスト
ここから先は、ソフトウェアテストを狭い意味で使います。テスト用のフレームワークを使ってテストコードを書き、実行することを指します。
先のWikipediaの項目のうち、Railsアプリケーションでテストコードを書く目的は、主に以下の2つです。
- Unit test(単体テスト) -- ソフトウェアを構成する小さな機能ごとのテスト
- Integration test(結合テスト) -- ソフトウェアの実際の動作の流れに沿った総合的なテスト
Integration testは「統合試験」などと訳されることもありますが、現実の開発現場で使われている「結合テスト」と呼ぶことにします。
Ruby on Railsにおける自動テスト
Ruby on Railsの場合、以下の2つのテストフレームワークがよく使われます。
RSpecはRails限定というわけではなく、Rails以外のRubyのテストにも使えます。
miniTestはRuby標準のテスト用ライブラリであり、Ruby on Railsでも標準とされています。
しかし現実には多くの開発会社ではRSpecが普及しています。RSpecはRailsには標準ではインストールされていないので、rspec gemを後からインストールして使います。
Railsの生みの親であるDHHはRSpecを好んでいないという事情もあるので、RailsでRSpecが標準セットに含まれることは当面なさそうです。
なお、BPSのRails開発ではRSpecが標準です。
テストをどこまで行うか - その2
RSpecをRailsにインストールすると、コントローラ・モデル・ビューなどにテスト用のファイルが自動で生成されます。テスト関連のgemをインストールすると他にもテスト用ファイルが作られたりします。
私も最初のうちは、これらをすべて埋めなければならないのかと思って考え込んでしまいました。しかし前述のとおり、テストは必要な部分をまず押さえるのがよいと考え直しました。
このあたりを一般化するのは難しいのですが、Railsアプリの場合たとえばコントローラのテストはそれほど重要でないことが多いと思われます。まずはモデルのテスト、続いてビューのテストが必要になることが多いのではないでしょうか。
なお、コントローラのコードはなるべくスリムに保ってモデルに書くようにするのが一般的です。モデルのコードが膨れ上がってきたらdecoratorなどを使ってコードを整理しましょう。
テストの動作について
私がRSpecを書き始めた当初わかっていなかったのは、「個別のテストコードが独立している」ということでした。
独立しているというのは、たとえばテストコードAとテストコードBがあった場合、AとBは関連しないということであり、またAとBを関連させるようなテストコードを書くべきではないということです。
より具体的には、テストコードAでなにがしかの値が設定されているということを、テストコードBであてにするべきではありません。初期値やデータは、テストコードごとに用意します。
RSpecをverboseモードで実行してみると、各テストコードを実行するたびにRailsのインスタンスが立ち上がり、終了してから次のテストコードでまたRailsのインスタンスが立ち上がり...といった具合になっています。
それでもテストコードが複雑になると、あるテストコードで設定した値や保存したファイルがその後のテストコードに影響してしまうことがあるかもしれません。その場合、テストコードAとテストコードBの実行順序を入れ替えると結果が変わってしまうでしょう。
またテストの内容によっては、毎回同じ結果を得るのが難しいものもあります。
テスト実行のランダム化
現在のRSpecは、各テストコードの実行順序はデフォルトでランダムになります。これは上述のようなテスト同士の無用な結合を検出するためでもあります。
以前のRSpecでは実行順序がデフォルトでランダムではなかったので、たまにランダムにしてみるとそれだけでテストがパスしなくなったりして焦ったことがあります。
追伸: テスト駆動開発(TDD)について
TDDは開発手法のひとつです。大ざっぱに言うと、先にテストを書き、続いてそのテストをパスするように本編のコードを書く、というものです。
TDDの是非についてはすぐ宗教論争になってしまうので深入りしませんが、個人的には「茶室に入るときに右足から入るか左足から入るか」という表千家と裏千家の違いのような、作法の話なのだと理解しています。
テストを書かなければいけないことはもう間違いないので、後はどちらの方法であってもコーディングのリズムに乗ることができればよいのかなという程度に考えています。
なおBPSの開発でTDDを採用するかどうかは開発速度と品質のトレードオフによります。Railsアプリの開発でTDDが必要になったことはこれまでありませんでしたが、モバイルアプリの開発では案件によっては行われることもあります。
参考
- AtMarkIT: いまさら聞けないTDD/BDD超入門
- Qiita: 使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」
- 電子書籍: Everyday Rails - RSpecによるRailsテスト入門
関連記事
- rspecで事前・事後の環境設定を切り替える
- FactoryGirlでtraitを使うとintegration test書くのが捗るという話
- [Rails] RSpecのモックとスタブの使い方
- [RSpec][Turnip] 一般的に使えるTurnipステップ集