Rails 4でコントローラの関心事をテストする方法
-
21-12-2019 - |
質問
Rails 4コントローラで使用されている場合の懸念のテストを処理する最善の方法は何ですか?些細な懸念があると言ってください。
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の単純化。
rspec http://www.relishapp。COM / RSPEC / RSPEC-RAILS / DOCS /コントローラスペック/匿名コントローラ
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
.
これがデフォルトで発生しない場合には、anonymous controller
を記述し、タイプを設定する必要があります。
私の答えは、@benjと@calinによってこれらよりも複雑になっているかもしれませんが、それはその利点を持っています。
describe Concerns::MyConcern, type: :controller do
described_class.tap do |mod|
controller(ActionController::Base) { include mod }
end
# your tests go here
end
.
まず、ActionController::Base
のサブクラスである匿名コントローラを使用することをお勧めします。このようにして、コントローラのいずれのコントローラからの隔離の懸念をテストすることができます。いくつかのメソッドをベースコントローラに定義することを期待している場合は、それらをスタブしてください。
さらに、コピーペーストエラーを回避するのに役立つように、関心モジュール名の書き換えを回避することを省略してください。残念ながら、ApplicationController
はdescribed_class
に渡されたブロックではアクセスできませんので、controller(ActionController::Base)
メソッドを使用して、Local変数に#tap
を格納する別のバインディングを作成します。これは、バージョン管理されたAPIを処理するときに特に重要です。そのような場合、新しいバージョンを作成するときに大量のコントローラをコピーすることは非常に一般的であり、そのような微妙なコピーペーストミスを間違いのある間違いをひどく簡単にします。
私は私のコントローラーの懸念をテストするためのより簡単な方法を使っています、これが正しい方法であるかどうかはわからないが私にとっては意味があるように思われ、あなたの含めコントローラーの範囲を使うのはそれほど簡単に思われました。この方法に問題があるかどうか教えてください。 サンプルコントローラ:
class MyController < BaseController
include MyConcern
def index
...
type = column_type(column_name)
...
end
.
終了
私のコントローラの関係:
module MyConcern
...
def column_type(name)
return :phone if (column =~ /phone/).present?
return :id if column == 'id' || (column =~ /_id/).present?
:default
end
...
end
.
懸念のためのスペックテスト:
require 'spec_helper'
describe SearchFilter do
let(:ac) { MyController.new }
context '#column_type' do
it 'should return :phone for phone type column' do
expect(ac.column_type('phone')).to eq(:phone)
end
it 'should return :id for id column' do
expect(ac.column_type('company_id')).to eq(:id)
end
it 'should return :id for id column' do
expect(ac.column_type('id')).to eq(:id)
end
it 'should return :default for other types of columns' do
expect(ac.column_type('company_name')).to eq(:default)
end
end
end
.