Question

Quelle est la meilleure façon de gérer les tests des problèmes lorsqu'ils sont utilisés dans les contrôleurs Rails 4 ?Dis que j'ai un souci trivial Citations.

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

Le comportement attendu lors du test est que tout contrôleur incluant ce problème obtiendrait ce problème. citations point final.

class ConversationController < ActionController::Base
    include Citations
end

Simple.

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

Mais quelle est la bonne manière de tester cette préoccupation de manière isolée ?

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

Malheureusement, cela échoue.

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

Ceci est un exemple artificiel.Dans mon application, j'obtiens une erreur différente.

RuntimeError:
  @routes is nil: make sure you set it in your test's setup method.
Était-ce utile?

La solution

Vous trouverez de nombreux conseils vous indiquant d'utiliser des exemples partagés et de les exécuter dans le cadre de vos contrôleurs inclus.

Personnellement, je trouve cela excessif et préfère effectuer des tests unitaires de manière isolée, puis utiliser des tests d'intégration pour confirmer le comportement de mes contrôleurs.

Méthode 1 :sans test de routage ni de réponse

Créez un faux contrôleur et testez ses méthodes :

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

Méthode 2 :réponse aux tests

Lorsque votre problème concerne le routage ou que vous devez tester la réponse, le rendu, etc.vous devez exécuter votre test avec un contrôleur anonyme.Cela vous permet d'accéder à toutes les méthodes et aides rspec liées au contrôleur :

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

Vous pouvez voir que nous devons envelopper le contrôleur anonyme dans un controller(ApplicationController).Si vos classes sont héritées d'une autre classe que ApplicationController, vous devrez l'adapter.

Aussi pour que cela fonctionne correctement vous devez déclarer dans votre spec_helper.rb déposer:

config.infer_base_class_for_anonymous_controllers = true

Note:continuez à tester que votre préoccupation est incluse

Il est également important de tester que votre classe concernée est incluse dans vos classes cibles, une ligne suffit :

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

Autres conseils

Simplifier la méthode 2 à partir de la réponse la plus votée.

je préfère le anonymous controller pris en charge dans rspec http://www.relishapp.com/rspec/rspec-rails/docs/controller-specs/anonymous-controller

Vous ferez:

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

Notez que vous devez décrire le ApplicationController et définissez le type au cas où cela ne se produirait pas par défaut.

Ma réponse peut paraître un peu plus compliquée que celles de @Benj et @Calin, mais elle a ses avantages.

describe Concerns::MyConcern, type: :controller do

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

  # your tests go here
end

Tout d'abord, je recommande l'utilisation d'un contrôleur anonyme qui est une sous-classe de ActionController::Base, pas ApplicationController ni aucun autre contrôleur de base défini dans votre application.De cette façon, vous pouvez tester le problème indépendamment de n'importe lequel de vos contrôleurs.Si vous vous attendez à ce que certaines méthodes soient définies dans un contrôleur de base, supprimez-les simplement.

De plus, c'est une bonne idée d'éviter de retaper le nom du module concerné, car cela permet d'éviter les erreurs de copier-coller.Malheureusement, described_class n'est pas accessible dans un bloc passé à controller(ActionController::Base), donc j'utilise #tap méthode pour créer une autre liaison qui stocke described_class dans une variable locale.Ceci est particulièrement important lorsque vous travaillez avec des API versionnées.Dans un tel cas, il est assez courant de copier un grand volume de contrôleurs lors de la création d'une nouvelle version, et il est alors terriblement facile de commettre une erreur de copier-coller aussi subtile.

J'utilise un moyen plus simple pour tester les problèmes de mon contrôleur, je ne sais pas si c'est la bonne méthode, mais cela me semble beaucoup plus simple que ce qui précède et cela a du sens, c'est en quelque sorte utiliser la portée de vos contrôleurs inclus.S'il vous plaît laissez-moi savoir s'il y a des problèmes avec cette méthode.contrôleur d'échantillon :

class MyController < BaseController
  include MyConcern

  def index
    ...

    type = column_type(column_name)
    ...
  end

fin

mon souci de contrôleur :

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

end

test de spécification pour préoccupation :

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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top