指定明确的“主题”时遇到麻烦?

发布于 2024-12-05 12:15:55 字数 1124 浏览 0 评论 0原文

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

I am using Ruby on Rails 3.0.9 and RSpect 2. I am trying to refactoring some spec file in the following way (in order to test with less code similar User class object attribute values):

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

However, if I run the above test I get the following error:

Failure/Error: it "should be whatever" do
  NoMethodError:
    undefined method `it' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_2::Nested_2:0x00000106ccee60>

What is the problem? How can I solve that?


UPDATE after the @Emily answer.

If in the above code I use context "foreach user" do ... instead of it "foreach user" do ... I get the following error:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

醉梦枕江山 2024-12-12 12:15:55

问题在于一个规范嵌套在另一个规范中。您需要将它的“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

The problem is having one spec nested within another. You need to replace it "foreach user" with context "foreach user".

Edited to add: After some investigation, it looks like helpers set with let are only available inside of the it "should ..." block, and not in the surrounding context. I'd recommend is trying to find a different structural solution. What the best solution is will depend on what you're actually trying to test. I'm guessing what you're trying to do is make sure the user is invalid when you remove any of the required attributes. In that case, what I've done is something like this:

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

If you're doing something that requires more complex differences in the instance you're creating, there may not be a clean way to do it with iteration. I don't think DRY is quite as essential in testing as it is in other parts of your code. There's nothing wrong with having three different contexts for the three user types, and a validity test in each context.

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
坐在坟头思考人生 2024-12-12 12:15:55

你正在混合和匹配各种 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

You're mixing and matching all sorts of rspec stuff. Here's your stuff, fixed:

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

I would do it this way:

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
  • If you want to have "context", then cool, but you can't have variables before your context inside of your context.
  • If you want to have a specification, then have one, but you can't net "it" statements

UPDATE WITH LEAST POSSIBLE CODE

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
放我走吧 2024-12-12 12:15:55

问题是用“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

The problem is that the helpers that are set with "let" do not exist outside of a example context.

What you're trying to do could be achieved as:

it "does something with all users" do
  [user1, user2, user3] do |user|
    user.valid?.should be_true
  end
end

Both contexts are different

Another way it might work (haven't tried it) it's like this:

context "for all users" do
  [:user1, :user2, :user3].each do |user|
    it "does something" do
      send(user).valid?.should be_true
    end
  end
end
々眼睛长脚气 2024-12-12 12:15:55

这应该有效。注意上下文是如何编写的,这将使测试的输出更清晰。通过这种方式编写,这意味着(对我来说)您应该分别对每个属性进行测试,但这是您的选择:

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

This should work. Note how the context is written, it will make the output of tests clearer. From writing it this way it implies (to me) that you should make a test for each attribute separately, but it's your choice:

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文