如何在 rspec 中编写 Rails 3.1 引擎控制器测试?
我已经编写了一个带有名称空间 Posts 的 Rails 3.1 引擎。因此,我的控制器位于 app/controllers/posts/ 中,我的模型位于 app/models/posts 等中。我可以很好地测试模型。一种型号的规格看起来像......
module Posts
describe Post do
describe 'Associations' do
it ...
end
并且一切正常。
然而,控制器的规格不起作用。 Rails 引擎安装在 /posts,但控制器是 Posts::PostController。因此,测试寻找的控制器路由是 posts/posts。
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
这会产生...
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
我在测试应用程序的路由文件中尝试了各种技巧...:命名空间等,但无济于事。
我该如何进行这项工作?看起来不会,因为引擎将控制器放在 /posts 处,但命名空间将控制器放在 /posts/posts 处以进行测试。
I have written a Rails 3.1 engine with the namespace Posts. Hence, my controllers are found in app/controllers/posts/, my models in app/models/posts, etc. I can test the models just fine. The spec for one model looks like...
module Posts
describe Post do
describe 'Associations' do
it ...
end
... and everything works fine.
However, the specs for the controllers do not work. The Rails engine is mounted at /posts, yet the controller is Posts::PostController. Thus, the tests look for the controller route to be posts/posts.
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
which yields...
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
I've tried all sorts of tricks in the test app's routes file... :namespace, etc, to no avail.
How do I make this work? It seems like it won't, since the engine puts the controller at /posts, yet the namespacing puts the controller at /posts/posts for the purpose of testing.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
我假设您正在使用虚拟 Rails 应用程序测试您的引擎,例如 enginex。
您的引擎应该安装在虚拟应用程序中:
在
spec/dummy/config/routes.rb
中:我的第二个假设是您的引擎是隔离的:
在
lib/posts.rb
中>:我不知道这两个假设是否真的需要,但这就是我自己的引擎的结构。
解决方法非常简单,而不是
使用这个
:posts
符号应该是引擎的名称,而不是它安装的路径。这是有效的,因为 get 方法参数直接传递到
ActionDispatch::Routing::RouteSet::Generator#initialize
(定义 此处),这又使用@named_route
来获取正确的路由来自Rack::Mount::RouteSet#generate
(请参阅此处 和 此处)。深入了解 Rails 内部结构很有趣,但相当耗时,我不会每天都这样做;-)。
华泰
I'm assuming you're testing your engine with a dummy rails app, like the one that would be generated by enginex.
Your engine should be mounted in the dummy app:
In
spec/dummy/config/routes.rb
:My second assumption is that your engine is isolated:
In
lib/posts.rb
:I don't know if these two assumptions are really required, but that is how my own engine is structured.
The workaround is quite simple, instead of this
use this
The
:posts
symbol should be the name of your engine and NOT the path where it is mounted.This works because the get method parameters are passed straight to
ActionDispatch::Routing::RouteSet::Generator#initialize
(defined here), which in turn uses@named_route
to get the correct route fromRack::Mount::RouteSet#generate
(see here and here).Plunging into the rails internals is fun, but quite time consuming, I would not do this every day ;-) .
HTH
我通过重写提供的
get
、post
、put
和delete
方法解决了这个问题,使得因此他们总是将use_route
作为参数传递。我使用伯努瓦的回答作为基础。谢谢哥们!
I worked around this issue by overriding the
get
,post
,put
, anddelete
methods that are provided, making it so they always passuse_route
as a parameter.I used Benoit's answer as a basis for this. Thanks buddy!
使用 rspec-rails
routes
指令:– RSpec Rails 2.14 官方文档。
Use the rspec-rails
routes
directive:– RSpec Rails 2.14 official docs.
基于此 回答 我选择了以下解决方案:
额外好处是,您不需要在每个控制器规范中都有
before(:each)
块。Based on this answer I chose the following solution:
The additional benefit is, that you don't need to have the
before(:each)
block in every controller-spec.没有或无法使用
isolate_namespace
时的问题解决方案:在控制器规范中,修复路由:
如果您不使用
isolate_namespace
,Rails 会将 _engine 添加到您的应用路由中>。Solution for a problem when you don't have or cannot use
isolate_namespace
:In controller specs, to fix routes:
Rails adds _engine to your app routes if you don't use
isolate_namespace
.我正在为我的公司开发一个 gem,它为我们正在运行的应用程序提供 API。我们仍然使用 Rails 3.0.9,以及最新的 Rspec-Rails (2.10.1)。我遇到了类似的问题,我在 Rails 引擎 gem 中定义了这样的路线。
我收到了这样的错误:
事实证明我只需要在下划线大小写中重新定义我的路线,以便 RSpec 可以匹配它。
我猜想 Rspec 控制器测试使用基于下划线大小写的反向查找,而如果您以驼峰大小写或下划线大小写定义路径,Rails 将设置并解释路由。
I'm developing a gem for my company that provides an API for the applications we're running. We're using Rails 3.0.9 still, with latest Rspec-Rails (2.10.1). I was having a similar issue where I had defined routes like so in my Rails engine gem.
I was getting an error like
It turns out I just needed to redefine my route in underscore case so that RSpec could match it.
I guess Rspec controller tests use a reverse lookup based on underscore case, whereas Rails will setup and interpret the route if you define it in camelcase or underscore case.
已经提到了添加
routes { MyEngine::Engine.routes }
,尽管可以为所有控制器测试指定它:并在
rails_helper.rb
中使用:It was already mentioned about adding
routes { MyEngine::Engine.routes }
, although it's possible to specify this for all controller tests:and use in
rails_helper.rb
: