Rails 4つのコントローラーで使用する場合の懸念事項のテストを処理する最良の方法は何ですか?些細な懸念事項Citations
があります。
module Citations
extend ActiveSupport::Concern
def citations ; end
end
テスト中の予想される動作は、この懸念を含むコントローラーがこのcitations
エンドポイントを取得することです。
class ConversationController < ActionController::Base
include Citations
end
シンプル。
ConversationController.new.respond_to? :yelling #=> true
しかし、この懸念を単独でテストする正しい方法は何ですか?
class CitationConcernController < ActionController::Base
include Citations
end
describe CitationConcernController, type: :controller do
it 'should add the citations endpoint' do
get :citations
expect(response).to be_successful
end
end
残念ながら、これは失敗します。
CitationConcernController
should add the citations endpoint (FAILED - 1)
Failures:
1) CitationConcernController should add the citations endpoint
Failure/Error: get :citations
ActionController::UrlGenerationError:
No route matches {:controller=>"citation_concern", :action=>"citations"}
# ./controller_concern_spec.rb:14:in `block (2 levels) in <top (required)>'
これは不自然な例です。私のアプリでは、別のエラーが発生します。
RuntimeError:
@routes is nil: make sure you set it in your test's setup method.
共有された例を使用し、含まれているコントローラーのスコープで実行するように指示する多くのアドバイスがあります。
私は個人的にそれが過剰殺害だと感じ、単体テストを単独で実行し、統合テストを使用してコントローラーの動作を確認することを好みます。
方法1:ルーティングまたは応答テストなし
偽のコントローラーを作成し、そのメソッドをテストします。
_describe MyControllerConcern do
before do
class FakesController < ApplicationController
include MyControllerConcern
end
end
after { Object.send :remove_const, :FakesController }
let(:object) { FakesController.new }
describe 'my_method_to_test' do
it { expect(object).to eq('expected result') }
end
end
_
方法2:応答のテスト
懸念事項にルーティングが含まれている場合、または応答やレンダリングなどをテストする必要がある場合は、匿名コントローラーでテストを実行する必要があります。これにより、コントローラー関連のすべてのrspecメソッドとヘルパーにアクセスできます。
_describe MyControllerConcern, type: :controller do
controller(ApplicationController) do
include MyControllerConcern
def fake_action; redirect_to '/an_url'; end
end
before { routes.draw {
get 'fake_action' => 'anonymous#fake_action'
} }
describe 'my_method_to_test' do
before { get :fake_action }
it { expect(response).to redirect_to('/an_url') }
end
end
_
controller(ApplicationController)
で匿名コントローラーをラップする必要があることがわかります。クラスがApplicationController
以外のクラスから継承されている場合、これを適応させる必要があります。
また、これが適切に機能するためには、spec_helper.rbファイルで宣言する必要があります。
_config.infer_base_class_for_anonymous_controllers = true
_
注:懸念事項が含まれていることをテストし続ける
また、懸念クラスがターゲットクラスに含まれていることをテストすることも重要です。1行で十分です。
_describe SomeTargetedController do
describe 'includes MyControllerConcern' do
it { expect(SomeTargetedController.ancestors.include? MyControllerConcern).to eq(true) }
end
end
_
最も投票された回答から方法2を簡略化します。
anonymous controller
rspecでサポート http://www.relishapp.com/rspec/rspec-Rails/docs/controller-specs/anonymous-controller
あなたは〜をします:
describe ApplicationController, type: :controller do
controller do
include MyControllerConcern
def index; end
end
describe 'GET index' do
it 'will work' do
get :index
end
end
end
ApplicationController
を記述し、デフォルトでこれが発生しない場合にタイプを設定する必要があることに注意してください。
私の答えは、@ Benjと@Calinによるこれらよりも少し複雑に見えるかもしれませんが、それには利点があります。
_describe Concerns::MyConcern, type: :controller do
described_class.tap do |mod|
controller(ActionController::Base) { include mod }
end
# your tests go here
end
_
まず、ApplicationController
ではなく_ActionController::Base
_のサブクラスである匿名コントローラーを使用することをお勧めします。アプリケーションで定義されている他のベースコントローラーも使用しません。このようにして、どのコントローラーからも隔離して懸念事項をテストできます。一部のメソッドがベースコントローラーで定義されると予想される場合は、それらをスタブするだけです。
さらに、コピーアンドペーストエラーを回避するのに役立つため、懸念モジュール名の再入力を避けることをお勧めします。残念ながら、controller(ActionController::Base)
に渡されたブロックでは_described_class
_にアクセスできないため、_#tap
_メソッドを使用して、ローカル変数に_described_class
_を格納する別のバインディングを作成します。これは、バージョン管理されたAPIを使用する場合に特に重要です。そのような場合、新しいバージョンを作成するときに大量のコントローラーをコピーすることは非常に一般的であり、そのような微妙なコピーアンドペーストの間違いは非常に簡単です。