web-dev-qa-db-ja.com

rspecでRails 3.1エンジンコントローラーテストを書くにはどうすればよいですか?

私はRails 3.1エンジンをPostsという名前空間で作成しました。したがって、コントローラーはapp/controllers/posts /にあり、モデルはapp/models/postsにあります。モデルは問題ありません。1つのモデルの仕様は次のようになります...

module Posts
  describe Post do
    describe 'Associations' do
      it ...
      end

...そしてすべてが正常に動作します。

ただし、コントローラーの仕様は機能しません。 Railsエンジンは/ postsにマウントされていますが、コントローラーはPosts :: PostControllerです。したがって、テストでは、コントローラールートがposts/postsであるかどうかを探します。

  describe "GET index" do
    it "assigns all posts as @posts" do
      Posts::Post.stub(:all) { [mock_post] }
       get :index
       assigns(:posts).should eq([mock_post])
    end
  end

これは...

  1) Posts::PostsController GET index assigns all posts as @posts
     Failure/Error: get :index
     ActionController::RoutingError:
     No route matches {:controller=>"posts/posts"}
     # ./spec/controllers/posts/posts_controller_spec.rb:16

テストアプリのroutesファイルであらゆる種類のトリックを試しました...:namespaceなど、役に立ちませんでした。

これを機能させるにはどうすればよいですか?エンジンはコントローラーを/ postsに配置するので、そうではないようですが、名前空間はテストの目的でコントローラーを/ posts/postsに配置します。

45

enginex によって生成されるようなダミーのRailsアプリを使用して、エンジンをテストしていると想定しています。

エンジンはダミーアプリにマウントする必要があります。

spec/dummy/config/routes.rb

Dummy::Application.routes.draw do
  mount Posts::Engine => '/posts-prefix'
end

私の2番目の仮定は、エンジンが分離されていることです。

lib/posts.rb

module Posts
  class Engine < Rails::Engine
    isolate_namespace Posts
  end
end

これらの2つの仮定が本当に必要かどうかはわかりませんが、それが私自身のエンジンの構造です。

これの代わりに、回避策は非常に簡単です

get :show, :id => 1

これを使って

get :show, {:id => 1, :use_route => :posts}

:posts記号は、エンジンの名前であり、マウントされているパスではありません。

これが機能するのは、getメソッドのパラメーターがActionDispatch::Routing::RouteSet::Generator#initialize(定義済み ここ )に直接渡され、@named_routeを使用してRack::Mount::RouteSet#generateから正しいルートを取得するためです(を参照)。 ここ および ここ )。

Rails内部に飛び込むのは楽しいですが、かなり時間がかかるので、毎日これを行うことはありません;-)。

HTH

42
Benoit Garret

この問題を回避するには、提供されているgetpostput、およびdeleteメソッドをオーバーライドして、常に_use_route_をパラメータとして。

私はこれの基礎としてブノワの答えを使用しました。ありがとう!

_module ControllerHacks
  def get(action, parameters = nil, session = nil, flash = nil)
    process_action(action, parameters, session, flash, "GET")
  end

  # Executes a request simulating POST HTTP method and set/volley the response
  def post(action, parameters = nil, session = nil, flash = nil)
    process_action(action, parameters, session, flash, "POST")
  end

  # Executes a request simulating PUT HTTP method and set/volley the response
  def put(action, parameters = nil, session = nil, flash = nil)
    process_action(action, parameters, session, flash, "PUT")
  end

  # Executes a request simulating DELETE HTTP method and set/volley the response
  def delete(action, parameters = nil, session = nil, flash = nil)
    process_action(action, parameters, session, flash, "DELETE")
  end

  private

  def process_action(action, parameters = nil, session = nil, flash = nil, method = "GET")
    parameters ||= {}
    process(action, parameters.merge!(:use_route => :my_engine), session, flash, method)
  end
end

RSpec.configure do |c|
  c.include ControllerHacks, :type => :controller
end
_
22
Ryan Bigg

Rspec-Rails routesディレクティブを使用します。

describe MyEngine::WidgetsController do
  routes { MyEngine::Engine.routes }

  # Specs can use the engine's routes & named URL helpers
  # without any other special code.
end

RSpec Rails 2.14公式ドキュメント

19
Mars

this の回答に基づいて、次の解決策を選択しました。

_#spec/spec_helper.rb
RSpec.configure do |config|
 # other code
 config.before(:each) { @routes = UserManager::Engine.routes }
end
_

追加の利点は、すべてのコントローラー仕様にbefore(:each)ブロックを含める必要がないことです。

5
wintersolutions

isolate_namespaceがない、または使用できない場合の問題の解決策:

module Posts
  class Engine < Rails::Engine
  end
end

コントローラの仕様では、ルートを修正するには:

get :show, {:id => 1, :use_route => :posts_engine}   

isolate_namespaceを使用しない場合、Railsはアプリルートに_engineを追加します。

2
Spajus

実行中のアプリケーションにAPIを提供するgemを会社向けに開発しています。 Rails 3.0.9を使用していますが、最新のRspec-Rails(2.10.1)を使用しています。Railsエンジンの宝石。

match '/companyname/api_name' => 'CompanyName/ApiName/ControllerName#apimethod'

次のようなエラーが発生していました

ActionController::RoutingError:
 No route matches {:controller=>"company_name/api_name/controller_name", :action=>"apimethod"}

RSpecがそれに一致するように、アンダースコアの場合にルートを再定義する必要があることがわかりました。

match '/companyname/api_name' => 'company_name/api_name/controller_name#apimethod'

Rspecコントローラーのテストでは、アンダースコアの場合に基づく逆ルックアップが使用されていると思いますが、キャメルケースまたはアンダースコアの場合でルートを定義すると、Railsがルートをセットアップして解釈します。

0
Jason at kabam