Pregunta

¿Cuál es la mejor manera de manejar las pruebas de preocupación cuando se utiliza en Rails 4 controladores?Decir que tengo una preocupación trivial Citations.

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

El comportamiento esperado en la prueba es que cualquier controlador que se incluye esta preocupación podría obtener este citations endpoint.

class ConversationController < ActionController::Base
    include Citations
end

Simple.

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

Pero ¿cuál es la forma correcta de prueba de esta preocupación en el aislamiento?

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

Por desgracia, esto no funciona.

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

Este es un ejemplo inventado.En mi aplicación, me sale un error diferente.

RuntimeError:
  @routes is nil: make sure you set it in your test's setup method.
¿Fue útil?

Solución

Usted encontrará muchos consejos, contando para uso compartido de ejemplos y ejecutar en el ámbito de su incluidos los controladores.

Yo personalmente creo que es más de-matar y prefieren realizar pruebas de unidad en el aislamiento, a continuación, utilizar la integración de pruebas para confirmar el comportamiento de mis controladores.

Método 1:sin enrutamiento o prueba de respuesta

Crear un falso controlador y probar sus métodos:

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étodo 2:pruebas de respuesta

Cuando su preocupación contiene enrutamiento o que usted necesita para la prueba de respuesta, de representación, etc...usted necesita para ejecutar la prueba con el anónimo controlador.Esto permite que usted para tener acceso a todas relacionadas con el mando de rspec, métodos y ayudantes:

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

Usted puede ver que tenemos para envolver el anónimo controlador en un controller(ApplicationController).Si las clases se hereda de otra clase de ApplicationController, usted tendrá que adaptarse a esto.

También para que esto funcione correctamente, usted debe declarar en su spec_helper.rb archivo:

config.infer_base_class_for_anonymous_controllers = true

Nota:mantener la prueba de que su preocupación está incluido

También es importante la prueba de que su preocupación clase se incluye en el objetivo de las clases, una línea es suficiente:

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

Otros consejos

La simplificación en el método 2 de la más votada respuesta.

Yo prefiero el anonymous controller apoyado en rspec http://www.relishapp.com/rspec/rspec-rails/docs/controller-specs/anonymous-controller

Usted va a hacer:

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

Tenga en cuenta que usted necesita para describir la ApplicationController y establezca el tipo en caso de que esto no suceda de forma predeterminada.

Mi respuesta puede parecer poco más complicado que estas por @Benj y @Calin, pero tiene sus ventajas.

describe Concerns::MyConcern, type: :controller do

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

  # your tests go here
end

Primero de todo, te recomiendo el uso de anónimo controlador que es una subclase de ActionController::Base, no ApplicationController ni cualquier otro controlador de base definidos en la aplicación.De esta manera usted es capaz de probar la preocupación en el aislamiento de cualquiera de sus controladores.Si usted espera que algunos de los métodos que se definen en un controlador de base, sólo la punta de ellos.

Además, es una buena idea para evitar la re-escribir la preocupación nombre del módulo, ya que ayuda a evitar copiar-pegar errores.Por desgracia, described_class no es accesible en un bloque pasa a controller(ActionController::Base), por lo que yo uso #tap método para crear otro enlace que almacena described_class en una variable local.Esto es especialmente importante cuando se trabaja con versiones de Api.En tal caso, es bastante común para copiar grandes volúmenes de controladores a la hora de crear una nueva versión, y es increíblemente fácil de hacer una sutil copiar-pegar error a continuación.

Estoy usando una forma más sencilla de probar mis inquietudes de mi controlador, no estoy seguro de si esta es la forma correcta, pero parecía mucho más sencilla que lo anterior y tiene sentido para mí, su tipo de uso en el alcance de sus controladores incluidos.Por favor, hágamelo saber si hay algún problema con este método. Controlador de muestra:

class MyController < BaseController
  include MyConcern

  def index
    ...

    type = column_type(column_name)
    ...
  end

final

Mi controlador se refiere:

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

end

Prueba de especificaciones para preocupación:

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top