我有一个简单的控制器测试,包含a.o.以下代码:
context "POST :create" do
before (:each) do
post :create, :user_id => @user.id,
:account => { .. some data ... }
end
it { response.status.should == 201 }
it { response.location.should be_present }
end
现在我想到了一种加速此测试的简单方法,并使用before(:all)
代替before(:each)
。在这种情况下,帖子只会进行一次。
所以我写道:
context "POST :create" do
before (:all) do
post :create, :user_id => @user.id,
:account => { .. some data ... }
end
it { response.status.should == 201 }
it { response.location.should be_present }
end
但后来我收到以下错误:
RuntimeError:
@routes is nil: make sure you set it in your test's setup method.
这是设计的吗?有没有办法规避它?
答案 0 :(得分:12)
我在rspec邮件列表上问了这个问题,得到了@dchelimsky自己的回复:
是。 rspec-rails包含rails的测试框架,其中没有before(:all)概念,因此在每个示例之前重置所有数据。即使我们想在rspec-rails中支持这一点(我也不支持),它首先需要更改rails。
因此在before(:all)
中无法进行控制器调用,它只能用于设置数据库或实例变量。
答案 1 :(得分:3)
我不确定这是不是一个好主意,但在||=
块中设置before(:each)
的类变量似乎有效:
describe PagesController do
describe "GET 'index'" do
before(:each) do
@@response ||= begin
get :index
response
end
end
it { @@response.should redirect_to(root_path) }
it { @@response.status.should == 301 }
it { @@response.location.should be_present }
end
end
另一种可能更清晰的方法是在单个规范中使用多个断言。添加:aggregate_failures
标记(或将断言包装在aggregate_failures {...}
块中)将分别打印每个失败,这提供了单独测试的粒度:
describe PagesController do
describe "GET 'index'" do
it "redirects to homepage", :aggregate_failures do
get :index
expect(response).to redirect_to(root_path)
expect(response.status).to eq(301)
expect(response.location).to be_present
end
end
end
答案 2 :(得分:3)
如果你想采用肮脏的全局变量方式并从速度提升中受益,你可以使用它,但警告。这种混乱的逻辑可以完成这项工作,但是却无法通过清晰易读的测试来达到驾驶目的。使用yield进行辅助重构比建议更多。
describe PagesController do
describe "GET 'index'" do
before(:each) do
GLOBAL ||= {}
@response = GLOBAL[Time.now.to_f] || begin
get :index
response
end
end
it { @response.should redirect_to(root_path) }
it { @response.status.should == 301 }
it { @response.location.should be_present }
end
end
您可以在spec / support中输入您选择的文件的重构如下
RSPEC_GLOBAL = {}
def remember_through_each_test_of_current_scope(variable_name)
self.instance_variable_set("@#{variable_name}", RSPEC_GLOBAL[variable_name] || begin
yield
end)
RSPEC_GLOBAL[variable_name] ||= self.instance_variable_get("@#{variable_name}")
end
因此,测试文件中的代码变为:
describe PagesController do
describe "GET 'index'" do
before(:each) do
remember_through_each_test_of_current_scope('memoized_response') do
get :index
response
end
end
it { @memoized_response.should redirect_to(root_path) }
it { @memoized_response.status.should == 301 }
it { @memoized_response.location.should be_present }
end
end
希望它有所帮助,并再次谨慎使用