2011-09-19 72 views
1

我使用Ruby on Rails的3.0.9和RSpect 2.我想通过以下方式重构了一些规范文件(为了用更少的代码相似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> 

问题是什么?我该如何解决这个问题?


@Emily答案后更新

如果在上面的代码中,我使用的context "foreach user" do ...代替it "foreach user" do ...我得到以下错误:

undefined local variable or method `user1' for #<Class:0x00000105310758> (NameError) 
+0

仅供参考,使用'subject'是一个红鲱鱼在这里。你从来没有真正使用你在你的例子中设置的主题。 – Emily

回答

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 
+0

什么是在第二个模块中重构代码的方法,以便编写更少的代码? – Backo

+0

用我能想到的最少代码更新 –

+1

您可以使用'let'将变量筛选到上下文中, – iain

2

的问题是有嵌套在另外一个规格。您需要用context "foreach user"替换it "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 
+0

我更新了问题。 – Backo

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 
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