ruby-on-rails-3 - matchers - rspec rails tutorial
¿Cómo escribo una prueba de controlador de motor Rails 3.1 en rspec? (6)
En base a this respuesta elegí la siguiente solución:
#spec/spec_helper.rb
RSpec.configure do |config|
# other code
config.before(:each) { @routes = UserManager::Engine.routes }
end
El beneficio adicional es que no necesita tener el bloque before(:each)
en cada controlador-especificación.
Escribí un motor Rails 3.1 con las publicaciones del espacio de nombres. Por lo tanto, mis controladores se encuentran en la aplicación / controladores / publicaciones /, mis modelos en la aplicación / modelos / publicaciones, etc. Puedo probar los modelos muy bien. La especificación para un modelo parece ...
module Posts
describe Post do
describe ''Associations'' do
it ...
end
... y todo funciona bien
Sin embargo, las especificaciones para los controladores no funcionan. El motor Rails está montado en / posts, pero el controlador es Posts :: PostController. Por lo tanto, las pruebas buscan que la ruta del controlador sea publicaciones / publicaciones.
describe "GET index" do
it "assigns all posts as @posts" do
Posts::Post.stub(:all) { [mock_post] }
get :index
assigns(:posts).should eq([mock_post])
end
end
cuyos rendimientos...
1) Posts::PostsController GET index assigns all posts as @posts
Failure/Error: get :index
ActionController::RoutingError:
No route matches {:controller=>"posts/posts"}
# ./spec/controllers/posts/posts_controller_spec.rb:16
He intentado todo tipo de trucos en el archivo de rutas de la aplicación de prueba ...: espacio de nombres, etc., en vano.
¿Cómo hago que esto funcione? Parece que no lo hará, ya que el motor pone el controlador en / posts, pero el espacio de nombres pone al controlador en / posts / posts con el propósito de probar.
Estoy desarrollando una joya para mi empresa que proporciona una API para las aplicaciones que estamos ejecutando. Estamos usando Rails 3.0.9 todavía, con los últimos Rspec-Rails (2.10.1). Estaba teniendo un problema similar donde había definido rutas como esa en mi gema del motor Rails.
match ''/companyname/api_name'' => ''CompanyName/ApiName/ControllerName#apimethod''
Me estaba dando un error como
ActionController::RoutingError:
No route matches {:controller=>"company_name/api_name/controller_name", :action=>"apimethod"}
Resultó que solo necesitaba redefinir mi ruta en guiones bajos para que RSpec pudiera hacerla coincidir.
match ''/companyname/api_name'' => ''company_name/api_name/controller_name#apimethod''
Supongo que las pruebas del controlador Rspec usan una búsqueda inversa basada en el caso de subrayado, mientras que Rails configurará e interpretará la ruta si la defines en camelcase o guión bajo.
He solucionado este problema anulando los métodos get
, post
, put
y delete
que se proporcionan, por lo que siempre pasan use_route
como parámetro.
Utilicé la respuesta de Benoit como base para esto. ¡Gracias amigo!
module ControllerHacks
def get(action, parameters = nil, session = nil, flash = nil)
process_action(action, parameters, session, flash, "GET")
end
# Executes a request simulating POST HTTP method and set/volley the response
def post(action, parameters = nil, session = nil, flash = nil)
process_action(action, parameters, session, flash, "POST")
end
# Executes a request simulating PUT HTTP method and set/volley the response
def put(action, parameters = nil, session = nil, flash = nil)
process_action(action, parameters, session, flash, "PUT")
end
# Executes a request simulating DELETE HTTP method and set/volley the response
def delete(action, parameters = nil, session = nil, flash = nil)
process_action(action, parameters, session, flash, "DELETE")
end
private
def process_action(action, parameters = nil, session = nil, flash = nil, method = "GET")
parameters ||= {}
process(action, parameters.merge!(:use_route => :my_engine), session, flash, method)
end
end
RSpec.configure do |c|
c.include ControllerHacks, :type => :controller
end
Solución para un problema cuando no tiene o no puede usar isolate_namespace
:
module Posts
class Engine < Rails::Engine
end
end
En las especificaciones del controlador, para arreglar las rutas:
get :show, {:id => 1, :use_route => :posts_engine}
Rails agrega _engine a las rutas de tu aplicación si no usas isolate_namespace
.
Supongo que estás probando tu motor con una aplicación de rieles ficticios, como la que generaría enginex .
Su motor debe estar montado en la aplicación ficticia:
En spec/dummy/config/routes.rb
:
Dummy::Application.routes.draw do
mount Posts::Engine => ''/posts-prefix''
end
Mi segunda suposición es que su motor está aislado:
En lib/posts.rb
:
module Posts
class Engine < Rails::Engine
isolate_namespace Posts
end
end
No sé si estas dos suposiciones son realmente necesarias, pero así es como mi propio motor está estructurado.
La solución es bastante simple, en lugar de esto
get :show, :id => 1
utilizar esta
get :show, {:id => 1, :use_route => :posts}
El símbolo :posts
debe ser el nombre de su motor y NO la ruta donde está montado.
Esto funciona porque los parámetros del método get se pasan directamente a ActionDispatch::Routing::RouteSet::Generator#initialize
(definido here ), que a su vez usa @named_route
para obtener la ruta correcta de Rack::Mount::RouteSet#generate
( mira here y here ).
Sumergirse en los componentes internos de los rieles es divertido, pero lleva mucho tiempo, no haría esto todos los días ;-).
HTH
Use la directiva de routes
rspec-rails:
describe MyEngine::WidgetsController do
routes { MyEngine::Engine.routes }
# Specs can use the engine''s routes & named URL helpers
# without any other special code.
end