Railsの情報サイトです。雑感RSpecとCapybaraでJavaScript/AjaxをテストするTweet2012/10/01RSpecはRubyのためのビヘイビア駆動開発(BDD)フレームワークで、Capybaraはブラウザの動きをシミュレートするRubyライブラリで、どちらもRubyGemsパッケージとして配布されています。Railsアプリケーションのテストを書く場合の定番の組み合わせといっていいでしょう。最近(2013年8月)、RSpec/Capybara入門という新連載を始めました。この記事でRSpec/Capybaraに興味を持った方は、こちらもどうぞ。メールでお問い合わせください。「微妙に説明通りではなかった」という経験をした方も同アドレスまで情報をお寄せいただけると助かります。この文章の内容は、随時更新しています。2012/11/02たとえば、ユーザーがあるRailsアプリケーションのトップページにアクセスして、a#touchmeをクリックしたら、次のページのp#messageに「Hello!」というテキストが含まれることをテストしたい場合、次のようなSpecファイルを書きます。have_content("Hello!")と書くこともできます。2.11で導入された新しい書き方です。(2012/06/15)によれば、今後は前者の書き方が推奨され、将来的には明示的に有効にしない限り、shouldは使えなくなるそうです。このRailsアプリケーションがJavaScript/Ajaxを利用していて、「span#touchmeをクリックしたら、p#messageの中に動的に「Hello!」というテキストが現れる」という仕様であった場合は、どうテストすればよいでしょうか。a#touchmeをspan#touchmeに変えるだけではだめです。CapybaraはJavaScriptを理解しないので、テストは失敗します。ここで登場するのがcapybara-webkitというドライバです。ChromeやSafariがこれを使っています。このドライバを使えばJavaScriptのテストが可能になります。「デュアルヘッド」という用語と同様にディスプレイを指します。capybara-webkitはディスプレイを操作しない、ということです。有名なSeleiumは実際にブラウザを起動して、APIを通じて操作します。CapybaraはデフォルトでSeleniumのドライバを内包しているのですぐに使えますが、テストするたびにブラウザが現れて画面がどんどん切り替わります。初めのうちは面白いし、失敗の原因が分かりやすいけれども、現実の開発で使っていると次第に煩わしくなります。installを実行します。システムにQtのライブラリがインストールされていないと動きません。qtでインストールします。Ubuntuの場合なら、libqt4-devパッケージをapt-getで入れておく必要があります。一つ準備作業が必要です。末尾に次の1行を追加してください。:webkitCapybaraはデフォルトでWebKitを使いません。trueオプションを付けると、その範囲のエグザンプル(RSpec用語では「テストケース」をこう言います)だけWebKitによるテストを行います。書き換え後のSpecファイルは次のようになります。endendこれでテストが通るようになります。単純なテストはこれでいいのですが、データベースが絡むと少々ややこしい問題に遭遇します。実は、WebKitを使用する場合、Capybaraは別スレッドでRailsアプリケーションを起動します。そのためRSpec側とRailsアプリケーション側は別々のデータベース接続を持つことになります。通常、RSpecは各エグザンプルの終わりにデータベースの状態を素早く元に戻すため、エグザンプルをトランザクションの中で実行します。エグザンプルが終わったらロールバックするんですね。トランザクションの中で行われたデータベース操作の結果は、コミットされるまで他のデータベースクライアントには見えません。RSpecの中でデータベースにレコードを追加しても、それがRailsアプリケーションには分からないのです。ブロックの中でデータベースを空にするというものです。そのための専用のRubyGemsパッケージdatabase_cleanerも存在しています。確かにこれでテストは通るようになりますが、トランザクションのロールバックを利用するよりもかなり遅いのが難点でした。中で素敵なハックを紹介したことで状況が変わりました。ままでJavaScriptのテストができます。spec/supportディレクトリに、次のような内容を持つ新規ファイルshared_connection.rbを作成します。末尾に次の1行を追加します。ActiveRecord::Base.connectiondatabase_cleanerのREADMEによれば、このハックを使うと「非決定論的な(non-deterministic)失敗を引き起こすことが報告されている」そうです。具体的にどういう報告があったのか調べられませんでしたが、この点に留意の上、この手法を採用するかどうか判断してください。いうエラーが出て止まることがあるそうです。}で置き換えれば問題は解決すると言っています。私はこの現象をまだ確認していません。私が携わっているRailsアプリケーション開発でこのハックを使ってみたところ、RSpecが途中でハングアウトする現象に遭遇しました。データベースにはPostgreSQLを使っています。いうエラーメッセージが現れ、あるいは何のエラーメッセージも出さずに、それ以上テストの実行が進まなくなります。試しに、データベースをMySQLに切り替えてみたところテストの実行は最後まで進んだので、PostgreSQL特有の問題かもしれません。私は上記のハックを2週間ほどMySQLベースのRails開発で使ってみましたが、大きな問題は起きませんでした。いうエラーが出ることがあります。Ajaxコールが完了するまで待ってからデータベースアクセスをするように書き換えれば問題は解消します。いう行を追加してください。Gehardのブログ記事を参考にしました。(2012/10/24)このハックはSporkにも対応しています。Sporkはテスト対象のアプリケーションを読み込んでおいて、テストケース(エグザンプル)ごとにforkしてくれます。その結果、テストがより堅牢になり、たいていは時間も短縮されます。Sporkの使い方は本稿のテーマではありませんが、簡単に説明します。installを実行します。---(以下省略)簡単に言えば、SporkはSpork.preforkブロックに書いてあるコードを1回だけ実行し、Spork.each_runブロックに書いてあるコードをエグザンプルを実行する直前に毎回実行します。---よりも下にあるコード(もともとのspec_helper.rbの中身)を全てSpork.preforkブロックに移動すればOKです。ActiveRecord::Base.connectionは、Spork.each_runブロックに移す必要があります。rspecを実行するのとは別のターミナルを開いて、そこでsporkコマンドを実行するとテストサーバが起動します。--drbオプションを付けてrspecコマンドを実行すると、Spork経由でテストが実行されます。常にSporkを使うなら.rspecファイルに--drbを追加しておくといいでしょう。Sporkによる時間短縮の効果はけっこう大きいです。したサンプルを作って試してみたところ、全体で約6秒かかっていたテストが約3秒になりました。XでQtをインストール手順を追加しました。発生する問題についてのStackOverflowからの引用を追加しました。PostgreSQLで発生する問題についての囲み記事を追加しました。Ajaxコールとの関連で発生する問題についての囲み記事を追加しました。