45

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

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

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

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

  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

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

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

4

7 に答える 7

42

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_routeRack::Mount::RouteSet#generate

レールの内部に飛び込むのは楽しいですが、かなり時間がかかるので、毎日はやりません ;-) .

HTH

于 2011-04-29T13:48:55.380 に答える
22

get提供されている、postput、およびメソッドをオーバーライドして、これらが常にパラメータとして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
于 2011-10-05T04:13:38.580 に答える
19

rspec-railsroutesディレクティブを使用します。

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 公式ドキュメント.

于 2013-07-09T00:10:44.650 に答える
5

この回答に基づいて、次のソリューションを選択しました。

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

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

于 2013-03-13T19:38:50.633 に答える
2

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

module Posts
  class Engine < Rails::Engine
  end
end

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

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

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

于 2012-11-20T15:58:08.000 に答える
0

実行中のアプリケーションに API を提供する会社用の gem を開発しています。Rails 3.0.9 と最新の Rspec-Rails (2.10.1) を引き続き使用しています。Railsエンジンのgemでそのようにルートを定義した同様の問題がありました。

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 はルートをセットアップして解釈します。

于 2012-05-25T18:11:54.213 に答える