unitarias rails pruebas hacer español crear controlador como ruby-on-rails rspec controller activesupport-concern

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.