我正在使用Ruby on Rails 3.0.9和RSpect 2.我试图以下列方式重构一些spec文件(为了测试类似User
类对象属性值的代码较少):
describe User do
let(:user1) { Factory(:user, :users_attribute_a => 'invalid_value') }
let(:user2) { Factory(:user, :users_attribute_b => 'invalid_value') }
let(:user3) { Factory(:user, :users_attribute_c => 'invalid_value') }
it "foreach user" do
[ user1, user2, user3 ].each do |user|
subject { user }
it "should be whatever"
user.should_not be_valid
...
end
end
end
end
但是,如果我运行上述测试,我会收到以下错误:
Failure/Error: it "should be whatever" do
NoMethodError:
undefined method `it' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_2::Nested_2:0x00000106ccee60>
有什么问题?我该如何解决?
更新。
如果在上面的代码中我使用context "foreach user" do ...
而不是it "foreach user" do ...
,我会收到以下错误:
undefined local variable or method `user1' for #<Class:0x00000105310758> (NameError)
答案 0 :(得分:2)
问题是将一个规范嵌套在另一个规范中。您需要将it "foreach user"
替换为context "foreach user"
。
编辑添加:经过一些调查后,看起来设置为let
的帮助程序只能在it "should ..."
块内部使用,而不能在周围环境中使用。我建议尝试寻找不同的结构解决方案。最好的解决方案取决于您实际尝试测试的内容。我猜你要做的是确保用户在删除任何必需属性时无效。在那种情况下,我所做的就是这样:
describe User do
let(:user_attributes){ Factory.attributes_for(:user) }
# Testing missing values aren't valid
[:name, :email, :phone].each do |required_attribute|
it "should not be valid without #{required_attribute}" do
User.new(user_attributes.except(required_attribute)).should_not be_valid
end
end
# Testing invalid values aren't valid
[[:email, 'not_an_email'], [:phone, 'not a phone']].each do |(attribute, value)|
it "should not be valid with bad value for #{attribute}" do
User.new(user_attributes.update(attribute => value)).should_not be_valid
end
end
end
如果你正在做的事情需要在你正在创建的实例中有更复杂的差异,那么迭代可能没有一种干净的方法。我不认为DRY在测试中与在代码的其他部分中一样重要。为三种用户类型设置三种不同的上下文,并在每种情况下进行有效性测试都没有错。
describe User do
context "with user1" do
subject{ Factory(:user, :users_attribute_a => 'invalid_value') }
it{ should_not be_valid }
end
context "with user2" do
subject{ Factory(:user, :users_attribute_b => 'invalid_value') }
it{ should_not be_valid }
end
context "with user3" do
subject{ Factory(:user, :users_attribute_c => 'invalid_value') }
it{ should_not be_valid }
end
end
答案 1 :(得分:1)
你正在混合和匹配各种各样的rspec东西。这是你的东西,修复:
describe User do
let(:user1) { Factory(:user, :users_attribute_a => 'invalid_value') }
let(:user2) { Factory(:user, :users_attribute_b => 'invalid_value') }
let(:user3) { Factory(:user, :users_attribute_c => 'invalid_value') }
it "should not be valid" do
[ user1, user2, user3 ].each do |user|
user.should_not be_valid
end
end
end
我会这样做:
describe User do
subject{Factory.build(:user)}
it "should not be valid with invalid users_attribute_a" do
subject.users_attribute_a = "invalid_value"
subject.should_not be_valid
end
it "should not be valid with invalid users_attribute_b" do
subject.users_attribute_b = "invalid_value"
subject.should_not be_valid
end
end
以最少的可能代码更新
describe User do
it "should not be valid with other attributes" do
{:users_attribute_a => 'invalid_value', :users_attribute_b => 'invalid_value', :users_attribute_c => 'invalid_value'}.each do |key, value|
Factory.build(:user, key => value).should_not be_valid
end
end
end
答案 2 :(得分:0)
问题是使用“let”设置的帮助程序不在示例上下文之外。
您要做的事情可以通过以下方式实现:
it "does something with all users" do
[user1, user2, user3] do |user|
user.valid?.should be_true
end
end
两种情境都不同
它可能起作用的另一种方式(尚未尝试过)就像这样:
context "for all users" do
[:user1, :user2, :user3].each do |user|
it "does something" do
send(user).valid?.should be_true
end
end
end
答案 3 :(得分:0)
这应该有效。注意如何编写上下文,它将使测试的输出更清晰。从这样写它就意味着(对我来说)你应该分别对每个属性进行测试,但这是你的选择:
describe User do
let!(:users) {
[:users_attribute_a, :users_attribute_b, :users_attribute_c].map do |a|
Factory(:user, => 'invalid_value')
end
}
context "Given a user" do
context "With an invalid value" do
subject { users }
it { subject.all?{|user| should_not be_valid }
end
end
end