在RSpec中是否存在与Cucumber的“情景”相同或者我使用RSpec的错误方法?

时间:2011-01-13 12:14:42

标签: ruby-on-rails rspec cucumber

我对Cucumber的情景的简洁性和实用性印象深刻,它们是测试不同情况的好方法。

e.g。 示例黄瓜方案

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方法?

  3. 哪个答案是对的,如果它有用,我该怎么做?

4 个答案:

答案 0 :(得分:21)

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

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

答案 1 :(得分:2)

我在问题RSpec Scenario Outlines: Multiple Test Cases中给出了一个适合RSpec的例子。我给出了一个可能的解决方案,但是如果你找到一个更好的解决方案,请告诉我。

答案 2 :(得分:1)

我不会以这种方式使用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来驱动应用程序的多个层次并不是一个好主意。换句话说,当您定义控制器时,您应该模拟与模型的交互等。

答案 3 :(得分:0)

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