質問

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
.

他のヒント

私の答えは、@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のサブクラスである匿名コントローラを使用することをお勧めします。このようにして、コントローラのいずれのコントローラからの隔離の懸念をテストすることができます。いくつかのメソッドをベースコントローラに定義することを期待している場合は、それらをスタブしてください。

さらに、コピーペーストエラーを回避するのに役立つように、関心モジュール名の書き換えを回避することを省略してください。残念ながら、ApplicationControllerdescribed_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
.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top