ruby on rails - pruebas - Cómo probar una preocupación del controlador en Rails 4
rspec español (4)
¿Cuál es la mejor manera de manejar las pruebas de problemas cuando se usa en los controladores Rails 4? Digamos que tengo una preocupación trivial Citations
.
module Citations
extend ActiveSupport::Concern
def citations ; end
end
El comportamiento esperado bajo prueba es que cualquier controlador que incluya esta preocupación obtendría este punto final de citations
.
class ConversationController < ActionController::Base
include Citations
end
Sencillo.
ConversationController.new.respond_to? :yelling #=> true
Pero, ¿cuál es la forma correcta de probar esta preocupación aisladamente?
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
Lamentablemente, esto falla.
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 artificial. En mi aplicación, recibo un error diferente.
RuntimeError:
@routes is nil: make sure you set it in your test''s setup method.
Encontrarás muchos consejos que te dicen que uses ejemplos compartidos y los ejecutes en el alcance de tus controladores incluidos.
Personalmente, considero que es excesivo y prefiero realizar pruebas unitarias de forma aislada, luego uso pruebas de integración para confirmar el comportamiento de mis controladores.
Método 1: sin enrutamiento o prueba de respuesta
Crea un controlador falso y prueba 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: prueba de respuesta
Cuando su inquietud contiene enrutamiento o necesita una prueba de respuesta, representación, etc., debe ejecutar su prueba con un controlador anónimo. Esto le permite obtener acceso a todos los métodos y ayudantes de rspec relacionados con el controlador:
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
Puede ver que tenemos que ajustar el controlador anónimo en un controller(ApplicationController)
. Si tus clases son heredadas de otra clase que ApplicationController
, necesitarás adaptar esto.
Además, para que esto funcione correctamente, debe declarar en su archivo spec_helper.rb :
config.infer_base_class_for_anonymous_controllers = true
Nota: siga probando que su preocupación está incluida
También es importante probar que su clase de preocupación está incluida en sus clases objetivo, una línea es suficiente:
describe SomeTargetedController do
describe ''includes MyControllerConcern'' do
it { expect(SomeTargetedController.ancestors.include? MyControllerConcern).to eq(true) }
end
end
Estoy usando una forma más simple de probar mis preocupaciones con el controlador, no estoy seguro de si esta es la manera correcta, pero parece mucho más simple que la anterior y tiene sentido para mí, es como usar el alcance de los controladores incluidos. Avíseme si hay algún problema con este método. controlador de muestra:
class MyController < V1::BaseController
include MyConcern
def index
...
type = column_type(column_name)
...
end
fin
mi preocupación del controlador:
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 especificación para su preocupación:
require ''spec_helper''
describe SearchFilter do
let(:ac) { V1::AppointmentsController.new }
context ''#column_type'' do
it ''should return :phone for phone type column'' do
expect(ac.column_type(''phone_daytime'')).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
Mi respuesta puede parecer un poco más complicada 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
Antes que nada, recomiendo el uso de un controlador anónimo que es una subclase de ActionController::Base
, no ApplicationController
ni ningún otro controlador base definido en su aplicación. De esta forma, puede probar la preocupación de forma aislada de cualquiera de sus controladores. Si espera que se definan algunos métodos en un controlador base, simplemente resúmalos.
Además, es una buena idea evitar volver a escribir el nombre del módulo de preocupación, ya que ayuda a evitar los errores de copiar y pegar. Desafortunadamente, described_class
no es accesible en un bloque pasado al controller(ActionController::Base)
, entonces uso el método #tap
para crear otro enlace que almacena described_class
en una variable local. Esto es especialmente importante cuando se trabaja con API versionadas. En tal caso, es bastante común copiar grandes volúmenes de controladores cuando se crea una nueva versión, y entonces es terriblemente fácil cometer un error tan sutil de copiar y pegar.
Simplificando en el método 2 de la respuesta más votada.
Prefiero el anonymous controller
admitido en rspec http://www.relishapp.com/rspec/rspec-rails/docs/controller-specs/anonymous-controller
Harás:
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 debe describir ApplicationController
y establecer el tipo en caso de que esto no ocurra de manera predeterminada.