ウェブサービスを作っています。

RSpec on Rails のバグっぽい挙動について


RSpec on Rails で redirect_to のテストを書いていると、

ActionController::MethodNotAllowed
Only get requests are allowed.

ActionController::MethodNotAllowed
Only get, put, and delete requests are allowed.

といった、謎の例外が発生することがあります。


簡単な (無意味な) Rails アプリを作りながら、説明します。

$ rails hoge
$ cd hoge
$ cd vendor/plugins
$ git clone git://github.com/dchelimsky/rspec.git
$ git clone git://github.com/dchelimsky/rspec-rails.git
$ rm -rf rspec/.git
$ rm -rf rspec-rails/.git
$ cd ../../
$ script/generate rspec


config/routes.rb

ActionController::Routing::Routes.draw do |map|
  map.resources :blogs
end


app/controllers/blogs_controller.rb

class BlogsController < ApplicationController
  def index
    redirect_to :action => :new
  end

  def new
    render :text => 'this is new'
  end
end


script/server して http://localhost:3000/blogs をブラウザで開けば、問題なく new にリダイレクトします。
そこで、テストを書いてみます。


spec/controllers/blogs_controller_spec.rb

require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

describe BlogsController, 'GET /blogs/index' do
  it 'は、blogs#new にリダイレクトすること' do
    get :index
    response.should redirect_to(:action => :new)
  end
end


実行!

$ script/spec spec/controllers/blogs_controller_spec.rb 
F

1)
ActionController::MethodNotAllowed in 'BlogsController GET /blogs/index は、blogs#new にリダイレクトすること'
Only get, put, and delete requests are allowed.
/Library/Ruby/Gems/1.8/gems/actionpack-2.1.1/lib/action_controller/routing/recognition_optimisation.rb:65:in `recognize_path'
./spec/controllers/blogs_controller_spec.rb:7:
script/spec:5:

Finished in 0.284136 seconds

1 example, 1 failure

・・・え?
ここで、spec ファイルの redirect_to(:action => :new) を名前付きルートで redirect_to(new_blog_url) と置き換えて


spec/controllers/blogs_controller_spec.rb

require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

describe BlogsController, 'GET /blogs/index' do
  it 'は、blogs#new にリダイレクトすること' do
    get :index
    response.should redirect_to(new_blog_url)
  end
end


再度実行。

$ script/spec spec/controllers/blogs_controller_spec.rb 
.

Finished in 0.177365 seconds
1 example, 0 failures

通った..


・・・どうやら、config/routes.rb の

map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'

を消して map.resources を使い、redirect_to マッチャでハッシュを引数にすると発生するようなんですが、それ以外でも発生するときがあって、正直よくわかりません。


とりあえずの解決策としては、上で示したように redirect_to マッチャで名前付きルートを使う、もしくは、
rspec-rails/lib/spec/rails/matchers/redirect_to.rb の Spec::Rails::Matchers::RedirectTo#path_hash で

ActionController::Routing::Routes.recognize_path path

となっている箇所を、

ActionController::Routing::Routes.recognize_path path, :method => :get

にすると、例外が発生しなくなります。


ActionController の redirect_to と、RSpec on Rails の redirect_to マッチャで、内部的に引数の扱い方が異なっているような気がして、そのへんが関係しているのかなー、と思ったりします。