RSpec 中是否有与 Cucumber 的“场景”等效的内容?或者我使用 RSpec 的方式错误?

发布于 2024-10-11 23:55:39 字数 1278 浏览 3 评论 0原文

Cucumber 场景的简洁性和实用性给我留下了深刻的印象,它们是测试大量不同案例的好方法。

例如示例 Cucumber 场景

Feature: Manage Users
In order to manage user details
As a security enthusiast
I want to edit user profiles only when authorized

Scenario Outline: Show or hide edit profile link
  Given the following user records
    | username | password | admin |
    | bob      | secret   | false |
    | admin    | secret   | true  |
  Given I am logged in as "<login>" with password "secret"
  When I visit profile for "<profile>"
  Then I should <action>

  Examples:
    | login | profile | action                 |
    | admin | bob     | see "Edit Profile"     |
    | bob   | bob     | see "Edit Profile"     |
    |       | bob     | not see "Edit Profile" |
    | bob   | admin   | not see "Edit Profile" |

(代码取自Ryan Bates' More on Cucumber Screencast)

RSpec 中有等效的吗?

我想在 RSpec 中做同样的事情,并通过将不同的测试减少到场景表中的一行。

虽然我可以自己编写代码来执行此操作,但我正在考虑它的事实让我想知道两件事

  1. 如果这有用,它可能已经存在,在这种情况下我该如何使用它?

  2. 如果它不存在,则表明不应该这样做,并且我错误地处理了这个问题,我应该如何重新考虑我的 RSpec 方法?

哪个答案是正确的,如果有用,我该怎么做?

I'm impressed at the brevity and usefulness of Cucumber's Scenarios, they're a great way to test a load of different cases.

e.g. example Cucumber scenario

Feature: Manage Users
In order to manage user details
As a security enthusiast
I want to edit user profiles only when authorized

Scenario Outline: Show or hide edit profile link
  Given the following user records
    | username | password | admin |
    | bob      | secret   | false |
    | admin    | secret   | true  |
  Given I am logged in as "<login>" with password "secret"
  When I visit profile for "<profile>"
  Then I should <action>

  Examples:
    | login | profile | action                 |
    | admin | bob     | see "Edit Profile"     |
    | bob   | bob     | see "Edit Profile"     |
    |       | bob     | not see "Edit Profile" |
    | bob   | admin   | not see "Edit Profile" |

(Code taken from Ryan Bates' More on Cucumber Screencast)

Is there an equivalent in RSpec?

I'd like to do the same thing in RSpec and to DRY up my code by reducing different tests down to a row in a scenario table.

Although I could write the code to do this myself, the fact that I'm considering it makes me wonder two things

  1. If this is useful it probably already exists in which case how do I use it?

  2. If it doesn't exist it suggests it shouldn't be done this way and that I'm approaching the problem incorrectly, how should I rethink my approach to RSpec?

Which answer is right and if it is useful, how do I do it?

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

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

发布评论

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

评论(4

不如归去 2024-10-18 23:55:40

我在问题 RSpec 场景大纲:多个测试用例。我给出了一种可能的解决方案,但如果您找到更好的解决方案,请告诉我。

I give an example appropriate for RSpec in the question RSpec Scenario Outlines: Multiple Test Cases. I give one possible solution, but please let me know if you find a better one.

败给现实 2024-10-18 23:55:40

我不会以这种方式使用 RSpec。 RSpec 应该用于将行为驱动到类中,一次一个小的行为。由于每种行为都是独特的,因此您应该使用不同的规范来定义它。

在上面的场景中,您可能有指定行为的规范,例如:

it "should allow user to edit his own profile"
it "should allow admin to edit other users profile"
it "should not allow non-admin to edit admin profile"
it "should not allow anonymous user to edit any profile"

还有一件事,使用 RSpec 驱动应用程序的多个层并不是一个好主意。换句话说,当您定义控制器时,您应该模拟与模型的交互等。

I would not use RSpec in this way. RSpec should be used to drive behavior into the class one small behavior at a time. Since each behavior is unique you should use a different spec to define it.

In the Scenario above you might have specs that specify behavior like:

it "should allow user to edit his own profile"
it "should allow admin to edit other users profile"
it "should not allow non-admin to edit admin profile"
it "should not allow anonymous user to edit any profile"

One more thing, it is not a good idea to use RSpec to drive through multiple layers of your application. In other words, when you are defining your controllers you should mock the interactions with your models, etc.

陌若浮生 2024-10-18 23:55:40

对于使用 RSpec 进行表驱动/参数化测试,现在有一些可能会有所帮助的宝石:

For table-driven / parameterized testing with RSpec there are now a couple of gems which might be helpful:

拥抱影子 2024-10-18 23:55:39

尝试以下方法。我喜欢它的结果。

describe StateDateMethods do
  before :each do
    @product = OpenStruct.new
    @product.extend StateDateMethods
  end

  def parse_date(unparsed_date_value)
    unless unparsed_date_value.nil?
      DateTime.strptime(unparsed_date_value, '%m/%d/%Y')
    end
  end

  context '#pre_order?' do
    examples = [
      # [visible_on, pre_order_on, for_sale_on] => method_result
      { :inputs => [nil, nil, nil], :expected => false },
      { :inputs => ['1/1/2001', nil, nil], :expected => false },
      { :inputs => ['1/1/2001', '1/1/2001', nil], :expected => true },
      { :inputs => ['1/1/2001', '1/2/2001', nil], :expected => true },
      { :inputs => ['1/1/2001', '1/1/2001', '1/2/2001'], :expected => false },
      { :inputs => ['1/1/2001', '1/1/2001', '1/1/3001'], :expected => true },
      { :inputs => ['1/1/2001', '1/1/3001', '1/2/3001'], :expected => false },
      { :inputs => ['1/1/3001', '1/1/3001', '1/2/3001'], :expected => false },
      { :inputs => ['1/1/2001', nil, '1/1/2001'], :expected => false },
      { :inputs => ['1/1/2001', nil, '1/1/3001'], :expected => false }
    ]
    examples.each do |example|
      inputs = example[:inputs]

      it "should return #{example[:expected].inspect} when visible_on == #{inputs[0].inspect}, pre_order_on == #{inputs[1].inspect}, for_sale_on == #{inputs[2].inspect}" do
        @product.visible_on = parse_date(inputs[0])
        @product.pre_order_on = parse_date(inputs[1])
        @product.for_sale_on = parse_date(inputs[2])

        @product.pre_order?.should == example[:expected]
      end
    end
  end
end

我认为这提供了两全其美的方法,因为它可以防止我重复自己,并且可以为每种情况创建不同的测试。

这是失败的样子:

....F.....

Failures:

  1) StateDateMethods#pre_order? should return false when visible_on == "1/1/2001", pre_order_on == "1/1/2001", for_sale_on == "1/2/2001"
     Failure/Error: @product.pre_order?.should == example[:expected]
       expected: false
            got: true (using ==)
     # ./spec_no_rails/state_date_methods_spec.rb:40:in `block (4 levels) in <top (required)>'

Finished in 0.38933 seconds
10 examples, 1 failure

Failed examples:

rspec ./spec_no_rails/state_date_methods_spec.rb:35 # StateDateMethods#pre_order? should return false when visible_on == "1/1/2001", pre_order_on == "1/1/2001", for_sale_on == "1/2/2001"

这是全绿色的样子:

..........

Finished in 0.3889 seconds
10 examples, 0 failures

Try the following approach. I like the way it turned out.

describe StateDateMethods do
  before :each do
    @product = OpenStruct.new
    @product.extend StateDateMethods
  end

  def parse_date(unparsed_date_value)
    unless unparsed_date_value.nil?
      DateTime.strptime(unparsed_date_value, '%m/%d/%Y')
    end
  end

  context '#pre_order?' do
    examples = [
      # [visible_on, pre_order_on, for_sale_on] => method_result
      { :inputs => [nil, nil, nil], :expected => false },
      { :inputs => ['1/1/2001', nil, nil], :expected => false },
      { :inputs => ['1/1/2001', '1/1/2001', nil], :expected => true },
      { :inputs => ['1/1/2001', '1/2/2001', nil], :expected => true },
      { :inputs => ['1/1/2001', '1/1/2001', '1/2/2001'], :expected => false },
      { :inputs => ['1/1/2001', '1/1/2001', '1/1/3001'], :expected => true },
      { :inputs => ['1/1/2001', '1/1/3001', '1/2/3001'], :expected => false },
      { :inputs => ['1/1/3001', '1/1/3001', '1/2/3001'], :expected => false },
      { :inputs => ['1/1/2001', nil, '1/1/2001'], :expected => false },
      { :inputs => ['1/1/2001', nil, '1/1/3001'], :expected => false }
    ]
    examples.each do |example|
      inputs = example[:inputs]

      it "should return #{example[:expected].inspect} when visible_on == #{inputs[0].inspect}, pre_order_on == #{inputs[1].inspect}, for_sale_on == #{inputs[2].inspect}" do
        @product.visible_on = parse_date(inputs[0])
        @product.pre_order_on = parse_date(inputs[1])
        @product.for_sale_on = parse_date(inputs[2])

        @product.pre_order?.should == example[:expected]
      end
    end
  end
end

I think this gives the best of both worlds, because it keeps me from repeating myself, and it creates a different test for each condition.

Here's what a failure looks like:

....F.....

Failures:

  1) StateDateMethods#pre_order? should return false when visible_on == "1/1/2001", pre_order_on == "1/1/2001", for_sale_on == "1/2/2001"
     Failure/Error: @product.pre_order?.should == example[:expected]
       expected: false
            got: true (using ==)
     # ./spec_no_rails/state_date_methods_spec.rb:40:in `block (4 levels) in <top (required)>'

Finished in 0.38933 seconds
10 examples, 1 failure

Failed examples:

rspec ./spec_no_rails/state_date_methods_spec.rb:35 # StateDateMethods#pre_order? should return false when visible_on == "1/1/2001", pre_order_on == "1/1/2001", for_sale_on == "1/2/2001"

And here's what all green looks like:

..........

Finished in 0.3889 seconds
10 examples, 0 failures
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文