Domanda

Qual è il modo migliore per gestire i test problematici quando utilizzati nei controller Rails 4?Diciamo che ho una preoccupazione banale Citations.

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

Il comportamento previsto durante il test è che qualsiasi controller che includa questo problema lo otterrebbe citations punto finale.

class ConversationController < ActionController::Base
    include Citations
end

Semplice.

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

Ma qual è il modo giusto per testare questa preoccupazione isolatamente?

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

Sfortunatamente, questo fallisce.

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

Questo è un esempio inventato.Nella mia app, ricevo un errore diverso.

RuntimeError:
  @routes is nil: make sure you set it in your test's setup method.
È stato utile?

Soluzione

Troverai molti consigli che ti dicono di utilizzare esempi condivisi ed eseguirli nell'ambito dei controller inclusi.

Personalmente lo trovo eccessivo e preferisco eseguire test unitari in isolamento, quindi utilizzare test di integrazione per confermare il comportamento dei miei controller.

Metodo 1:senza routing o test di risposta

Crea un controller falso e testa i suoi metodi:

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

Metodo 2:testare la risposta

Quando la tua preoccupazione riguarda il routing o devi testare la risposta, il rendering ecc...devi eseguire il test con un controller anonimo.Ciò ti consente di accedere a tutti i metodi e gli helper rspec relativi al controller:

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

Puoi vedere che dobbiamo racchiudere il controller anonimo in a controller(ApplicationController).Se le tue classi sono ereditate da un'altra classe rispetto a ApplicationController, dovrai adattarlo.

Anche perché questo funzioni correttamente devi dichiararlo nel tuo spec_helper.rb file:

config.infer_base_class_for_anonymous_controllers = true

Nota:continua a verificare che la tua preoccupazione sia inclusa

È anche importante verificare che la classe di interesse sia inclusa nelle classi target, è sufficiente una riga:

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

Altri suggerimenti

Simplicare sul metodo 2 dalla risposta più votata.

Preferisco il anonymous controller supportato in Rspec http://www.relishApp.COM / RSPEC / RSPEC-Rails / Docs / Controller-Specifiche / Controller anonimo

lo farai:

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
.

Nota che è necessario descrivere il ApplicationController e impostare il tipo nel caso in cui ciò non accada per impostazione predefinita.

La mia risposta potrebbe sembrare un po 'più complicata di questi di @benj e @calin, ma ha i suoi vantaggi.

describe Concerns::MyConcern, type: :controller do

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

  # your tests go here
end
.

Prima di tutto, raccomando l'uso di controller anonimo che è una sottoclasse di ActionController::Base, non ApplicationController né qualsiasi altro controller di base definito nell'applicazione. In questo modo sei in grado di testare la preoccupazione in isolamento da qualsiasi dei tuoi controller. Se ti aspetti che alcuni metodi vengano definiti in un controller di base, solo stubli.

Inoltre, è una buona idea evitare di ri-digitare il nome del modulo del modulo in quanto aiuta a evitare errori di copia-incolla. Sfortunatamente, described_class non è accessibile in un blocco passato a controller(ActionController::Base), quindi utilizzo il metodo #tap per creare un altro legame che memorizza described_class in una variabile locale. Questo è particolarmente importante quando si lavora con le API con versione. In tal caso è abbastanza comune copiare un grande volume di controllori durante la creazione di una nuova versione, ed è terribilmente facile fare un errore di pasta di copia così sottile.

Sto usando un modo più semplice per testare i miei problemi di controllo, non sono sicuro se questo è il modo corretto, ma sembrava molto più semplice che quanto sopra e abbia senso per me, il suo tipo di utilizzo del campo di applicazione dei controller inclusi.Per favore fatemi sapere se ci sono problemi con questo metodo. Controller di esempio:

class MyController < BaseController
  include MyConcern

  def index
    ...

    type = column_type(column_name)
    ...
  end
.

fine

Il mio controller riguarda:

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

end
.

Test delle specifiche per preoccupazione:

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
.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top