Есть ли в RSpec эквивалент «сценариев» от Cucumber или я неправильно использую RSpec? - PullRequest
16 голосов
/ 13 января 2011

Я впечатлен краткостью и полезностью сценариев 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" |

(Код взят из Подробнее Райана Бейтса о скриншоте из огурца )

Есть ли эквивалент в RSpec?

Я бы хотел сделать то же самое в RSpec и высушить мой код, сократив количество различных тестов до строки в таблице сценариев.

Хотя я мог бы написать код, чтобы сделать это сам, тот факт, что я рассматриваю это, заставляет меня задуматься о двух вещах

  1. Если это полезно, возможно, оно уже существует, и как мне его использовать?

  2. Если его не существует, он предполагает, что этого не следует делать, и что я неправильно подхожу к проблеме, как мне переосмыслить свой подход к RSpec?

Какой ответ правильный и если он полезен, как мне это сделать?

Ответы [ 4 ]

21 голосов
/ 03 декабря 2011

Попробуйте следующий подход.Мне нравится, как это получилось.

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
2 голосов
/ 02 апреля 2011

Я приведу пример, подходящий для RSpec, в вопросе Контуры сценария RSpec: несколько тестовых случаев .Я даю одно возможное решение, но, пожалуйста, дайте мне знать, если вы найдете лучшее.

1 голос
/ 15 января 2011

Я бы не использовал 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 для прохождения нескольких уровней вашего приложения.Другими словами, когда вы определяете свои контроллеры, вы должны высмеивать взаимодействия с вашими моделями и т. Д.

0 голосов
/ 05 июня 2019

Для табличного / параметризованного тестирования с RSpec теперь есть пара гемов, которые могут быть полезны:

  • RSpec :: WithParams - микро-DSL для параметризованного тестирования (раскрытие: я автор)
  • RSpec :: Parameterized - Поддерживает простой параметризованный синтаксис теста в RSpec
...