Frage

Was ist der beste Weg, um das Testen von Bedenken bei der Verwendung in Rails 4-Controllern zu handhaben?Angenommen, ich habe ein triviales Anliegen Citations.

module Citations
    extend ActiveSupport::Concern
    def citations ; end
end

Das erwartete Verhalten im Test besteht darin, dass jeder Controller, der dieses Problem aufweist, dies erhalten würde citations Endpunkt.

class ConversationController < ActionController::Base
    include Citations
end

Einfach.

ConversationController.new.respond_to? :yelling #=> true

Aber wie kann man dieses Problem isoliert testen?

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

Leider scheitert dies.

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)>'

Dies ist ein erfundenes Beispiel.In meiner App erhalte ich eine andere Fehlermeldung.

RuntimeError:
  @routes is nil: make sure you set it in your test's setup method.
War es hilfreich?

Lösung

Sie werden viele Ratschläge finden, die Sie dazu auffordern, gemeinsam genutzte Beispiele zu verwenden und diese im Rahmen Ihrer enthaltenen Controller auszuführen.

Ich persönlich finde es übertrieben und bevorzuge es, Unit-Tests isoliert durchzuführen, anstatt Integrationstests zu verwenden, um das Verhalten meiner Controller zu bestätigen.

Methode 1:ohne Routing- oder Antworttests

Erstellen Sie einen gefälschten Controller und testen Sie seine Methoden:

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

Methode 2:Testantwort

Wenn Ihr Problem das Routing betrifft oder Sie die Antwort, das Rendering usw. testen müssen ...Sie müssen Ihren Test mit einem anonymen Controller ausführen.Dadurch erhalten Sie Zugriff auf alle Controller-bezogenen Rspec-Methoden und -Helfer:

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

Sie können sehen, dass wir den anonymen Controller in einen einschließen müssen controller(ApplicationController).Wenn Ihre Klassen von einer anderen Klasse geerbt werden ApplicationController, müssen Sie dies anpassen.

Damit dies ordnungsgemäß funktioniert, müssen Sie dies auch in Ihrem Konto angeben spec_helper.rb Datei:

config.infer_base_class_for_anonymous_controllers = true

Notiz:Testen Sie weiterhin, ob Ihr Anliegen darin enthalten ist

Es ist auch wichtig zu testen, ob Ihre Anliegenklasse in Ihren Zielklassen enthalten ist. Eine Zeile reicht aus:

describe SomeTargetedController do
  describe 'includes MyControllerConcern' do
    it { expect(SomeTargetedController.ancestors.include? MyControllerConcern).to eq(true) }
  end
end

Andere Tipps

Vereinfachen Sie Methode 2 anhand der Antwort mit den meisten Stimmen.

Ich bevorzuge die anonymous controller unterstützt in rspec http://www.relishapp.com/rspec/rspec-rails/docs/controller-specs/anonymous-controller

Du wirst machen:

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

Beachten Sie, dass Sie das beschreiben müssen ApplicationController und legen Sie den Typ fest, falls dies nicht standardmäßig geschieht.

Meine Antwort sieht vielleicht etwas komplizierter aus als die von @Benj und @Calin, aber sie hat ihre Vorteile.

describe Concerns::MyConcern, type: :controller do

  described_class.tap do |mod|
    controller(ActionController::Base) { include mod }
  end

  # your tests go here
end

Zunächst empfehle ich die Verwendung eines anonymen Controllers, der eine Unterklasse von ist ActionController::Base, nicht ApplicationController noch ein anderer in Ihrer Anwendung definierter Basiscontroller.Auf diese Weise können Sie das Problem isoliert von jedem Ihrer Controller testen.Wenn Sie erwarten, dass einige Methoden in einem Basiscontroller definiert werden, stubben Sie sie einfach.

Darüber hinaus ist es ratsam, den Namen des betreffenden Moduls nicht erneut einzugeben, da dadurch Fehler beim Kopieren und Einfügen vermieden werden.Bedauerlicherweise, described_class ist in einem übergebenen Block nicht zugänglich controller(ActionController::Base), also benutze ich #tap Methode zum Erstellen einer weiteren Bindung, die speichert described_class in einer lokalen Variablen.Dies ist besonders wichtig, wenn Sie mit versionierten APIs arbeiten.In einem solchen Fall ist es durchaus üblich, beim Erstellen einer neuen Version eine große Anzahl von Controllern zu kopieren, und es ist dann furchtbar leicht, einen so subtilen Fehler beim Kopieren und Einfügen zu machen.

Ich verwende eine einfachere Methode, um meine Controller-Bedenken zu testen. Ich bin mir nicht sicher, ob dies die richtige Methode ist, aber sie erschien mir viel einfacher als die oben genannte und macht für mich Sinn, da sie den Umfang Ihrer mitgelieferten Controller nutzt.Bitte lassen Sie mich wissen, wenn es Probleme mit dieser Methode gibt.Probencontroller:

class MyController < BaseController
  include MyConcern

  def index
    ...

    type = column_type(column_name)
    ...
  end

Ende

Mein Controller-Anliegen:

module MyConcern
  ...
  def column_type(name)
    return :phone if (column =~ /phone/).present?
    return :id if column == 'id' || (column =~ /_id/).present?
   :default
  end
  ...

end

Spezifikationstest zur Besorgnis:

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
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top