Cómo probar un Controlador de Preocupación en Rails 4
-
21-12-2019 - |
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.
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