irohiroki's blog

Ruby, Rails, and Web technologies

Quick link to a sample app.

Autotestはテスト駆動開発において欠かせないツールです。しかし、特にRuby 1.9ではRspecの起動の遅く、イライラしている人も多いでしょう。

Rspecの起動を早くするツールにSporkがありますが、以下のような問題があります:

  • 製品コードを更新してもリロードしてくれない(ぇ
  • ruby-debugが使えない

このエントリではこれらの問題を解決していきます。目指すのは次のような環境です:

  • appの下はもちろん、configの下を更新した場合もリロードしてテストしてくれる
  • 製品コードでもテストコードでも、debuggerと書いたらruby-debugが使える

Spork

まずはベースの環境から作りましょう。なお、ここで使うのはMac上のRails 3.0.3とRuby 1.9.2です。

gem install rails
rails new my_app

最初にRSpecとautotest、sporkを入れるために、Gemfileに以下の行を加えます。

gem 'rspec-rails'
gem 'autotest-rails'
gem 'spork'

Gemfileを編集したらbundleしておきます。

bundle

RSpecをインストールします:

rails g rspec:install

Sporkに必要な準備をします:

spork --bootstrap

ここで、表示される指示に従ってspec/spec_helper.rbを編集します。下のようにしておけばよいでしょう。

require 'rubygems'
require 'spork'

Spork.prefork do
  ENV["RAILS_ENV"] ||= 'test'
  require File.expand_path("../../config/environment", __FILE__)
  require 'rspec/rails'

  Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}

  RSpec.configure do |config|
    config.mock_with :rspec
    config.fixture_path = "#{::Rails.root}/spec/fixtures"
    config.use_transactional_fixtures = true
  end
end

Spork.each_run do
end

またSporkを有効にするため、rspec:installによって生成された.rspecファイルに--drbを加えておきます。

--color --drb

これで素のAutotestとSporkの環境はできました。適当なコードをscaffoldして試してみてもいいでしょう。

rails g scaffold user name:string
rake db:migrate RAILS_ENV=test

sporkを起動し、別のターミナルでautotestも実行します。

spork
autotest

さらに別のターミナルでテストコードを失敗するように修正するとすぐさまautotestが赤くなります。しかし、製品コードを壊した場合、テストは走りますが緑のままです。

リロードさせる

製品コードのリロードは、実は次の2つの問題に分けられます。

  1. app下のファイルを修正した時
  2. それ以外

1はRuby on Rails Tutorial: Learn Rails by Example | Ruby on Rails 3 Tutorial book and screencasts | Static Pagesに回避方法が載っています。まず下のようにspec/spec_helper.rbのRSpec.configureのブロックに1行加えます。

diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 9fd4d4a..0d793e4 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -12,6 +12,8 @@ Spork.prefork do
     config.mock_with :rspec
     config.fixture_path = "#{::Rails.root}/spec/fixtures"
     config.use_transactional_fixtures = true
+
+    ActiveSupport::Dependencies.clear
   end
 end
 

そしてconfig/application.rbも下のように修正します:

diff --git a/config/application.rb b/config/application.rb
index 590b85c..02b3ca4 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -38,5 +38,13 @@ module MyApp
 
     # Configure sensitive parameters which will be filtered from the log file.
     config.filter_parameters += [:password]
+
+    ### Part of a Spork hack. See http://bit.ly/arY19y
+    if Rails.env.test?
+      initializer :after => :initialize_dependency_mechanism do
+        # Work around initializer in railties/lib/rails/application/bootstrap.rb
+        ActiveSupport::Dependencies.mechanism = :load
+      end
+    end
   end
 end

これでapp以下を修正した場合でもリロードしてテストしてくれます。

app以外、例えばconfig/application.rbやconfig/initializersの中は、rspecの起動を速くするためにプリロードしているので、sporkの再起動で解決します。これを自動的にやってくれるのがguard-sporkです。まずguard-sporkとFSEvent APIのRubyバインディングであるrb-fseventをインストールします。ただしここで使うguard-sporkは、後述するruby-debugのために私が改造したものです。

gem 'guard-spork', :git => 'https://github.com/irohiroki/guard-spork.git'
gem 'rb-fsevent'

加えたらbundleしてguard-sporkの初期化をします。

bundle
bundle exec guard init spork

そしてguardを起動。

bundle exec guard start

sporkはguardが起動してくれます。config/application.rbなどを更新するとguardがsporkを再起動します。

ところでguardが監視してくれるファイルの中にはconfig/routes.rbがないのですが、これは下の修正をすることでテストに反映できるようになります。

diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 6f6a223..cffc9f5 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -19,4 +19,5 @@ Spork.prefork do
 end
 
 Spork.each_run do
+  MyApp::Application.reload_routes!
 end

ruby-debugを使う

この時点では、rdebugを起動してコードの中にdebuggerと書いても止まってくれません。なぜなら、実際のテストはDRbサーバであるsporkで動いているからです(たぶん)。

この問題に対して、sporkの作者のTim Harper氏がrdebugのセッションをフォワードするプロキシを書いてくれました。これを使うと、debuggerが現れたときにsporkのターミナルにrdebugのプロンプトが出ます(autotestのターミナルではない)。ただし、一箇所RuntimeErrorが出るところがあるので、ここでは私の修正版を使います。まずcurlなどでプロキシモジュールをspecディレクトリに入れます。

curl https://gist.github.com/raw/814375/ccbe163559376e109e30574b1086a32fb626e374/spork-ruby-debug.rb > spec/spork-ruby-debug.rb

そしてそれをspec/spec_helper.rbからrequireします。

diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 0d793e4..6f6a223 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,5 +1,6 @@
 require 'rubygems'
 require 'spork'
+require File.expand_path("../spork-ruby-debug", __FILE__)
 
 Spork.prefork do
   ENV["RAILS_ENV"] ||= 'test'

もちろんruby-debugもインストールしておきましょう。Gemfileに下の行を加えてbundleします。

gem 'ruby-debug19'
bundle

これで完成です。bundle execを付けるのを忘れずにguardを起動してください。

bundle exec guard start

実はオリジナルのguard-sporkだとsporkのプロセスがbackgroundへ回されてしまってrdebugのプロンプトが取れないのですが、そこは私の修正版で回避してあります。

以上の手順で作った環境をgithubに置きましたのでよろしければ参考にしてください。

Published on 08/02/2011 at 05h16 under . Tags ,

(このエントリは、第58回 Rails勉強会@東京で発表した内容をまとめたものです。)

OmniAuthは、TwitterやGoogleなど様々な認証サービスプロバイダを統一したインターフェースで使えるようにしてくれるgemです。非常に便利なのでさっそく使おうとしたのですが、テストの書き方がわからなくて躓いたので、調べてわかったことやサンプルコードを公開します。

なお、OmniAuthは単体でも使えるのですが、伝統的なユーザ名とパスワードによる認証もサポートすることを想定して、Deviseと併用する構成となっています。

ポイントは以下の通りです:

  • TwitterはOAuth 1.0
  • 認証の過程でTwitterに3回のHTTPリクエストが飛ぶ
  • それらをスタブで受ける

発表で使った資料を下に貼っておきます。最低限のことしか書かれていませんが、参考になれば。

Published on 18/12/2010 at 07h09 under . Tags , , ,

Powered by Typo – Thème Frédéric de Villamil | Photo L. Lemos