指定明确的'主题'的麻烦?

时间:2011-09-19 18:12:56

标签: ruby-on-rails ruby ruby-on-rails-3 rspec rspec2

我正在使用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>

有什么问题?我该如何解决?


@Emily 回答后

更新

如果在上面的代码中我使用context "foreach user" do ...而不是it "foreach user" do ...,我会收到以下错误:

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

4 个答案:

答案 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
  • 如果你想拥有“上下文”,那么很酷,但你不能在上下文中使用变量。
  • 如果你想要一个规范,那就有一个,但是你不能净“it”语句

以最少的可能代码更新

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