RSpec: очікуйте зміни кількох


86

Я хочу перевірити наявність багатьох змін у моделі під час надсилання форми в специфікації функції. Наприклад, я хочу переконатись, що ім’я користувача було змінено з X на Y, а зашифрований пароль змінено на будь-яке значення.

Я знаю, що вже є кілька питань щодо цього, але я не знайшов для мене відповідної відповіді. Найточніша відповідь здається ChangeMultipleзбігом тут Майкла Джонстона: чи можливо, щоб RSpec очікував змін у двох таблицях? . Його мінус полягає в тому, що перевіряється лише наявність явних змін від відомих значень до відомих значень.

Я створив якийсь псевдокод про те, як, на мою думку, може виглядати кращий збіг:

expect {
  click_button 'Save'
}.to change_multiple { @user.reload }.with_expectations(
  name:               {from: 'donald', to: 'gustav'},
  updated_at:         {by: 4},
  great_field:        {by_at_leaset: 23},
  encrypted_password: true,  # Must change
  created_at:         false, # Must not change
  some_other_field:   nil    # Doesn't matter, but want to denote here that this field exists
)

Я також створив основний скелет ChangeMultipleзбігу таким чином:

module RSpec
  module Matchers
    def change_multiple(receiver=nil, message=nil, &block)
      BuiltIn::ChangeMultiple.new(receiver, message, &block)
    end

    module BuiltIn
      class ChangeMultiple < Change
        def with_expectations(expectations)
          # What to do here? How do I add the expectations passed as argument?
        end
      end
    end
  end
end

Але зараз я вже отримую цю помилку:

 Failure/Error: expect {
   You must pass an argument rather than a block to use the provided matcher (nil), or the matcher must implement `supports_block_expectations?`.
 # ./spec/features/user/registration/edit_spec.rb:20:in `block (2 levels) in <top (required)>'
 # /Users/josh/.rvm/gems/ruby-2.1.0@base/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:268:in `load'
 # /Users/josh/.rvm/gems/ruby-2.1.0@base/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:268:in `block in load'

Будь-яка допомога у створенні цього спеціального збігу дуже вдячна.

Відповіді:


183

У RSpec 3 ви можете налаштувати кілька умов одночасно (тому єдине правило очікування не порушено). Це буде виглядати приблизно так:

expect {
  click_button 'Save'
  @user.reload
}.to change { @user.name }.from('donald').to('gustav')
 .and change { @user.updated_at }.by(4)
 .and change { @user.great_field }.by_at_least(23}
 .and change { @user.encrypted_password }

Однак це не повне рішення - наскільки моє дослідження пішло, поки що немає простого способу зробити це and_not. Я також не впевнений щодо вашої останньої перевірки (якщо це не має значення, навіщо її перевіряти?). Звичайно, ви повинні мати змогу обернути його у своєму індивідуальному збігу .


6
якщо ви хочете очікувати, що багато речей не зміниться, просто використовуйте.and change { @something }.by(0)
stevenspiel

1
Чи можете ви додати другий приклад із усіма дужками? Мені важко зрозуміти, які методи прикуті до
ланцюга

Моя відповідь працює на Ruby 2 і, схоже, працює з .should_notусіма, хто цього потребує
Zack Morris

37

Якщо ви хочете перевірити, що кілька записів не було змінено, ви можете інвертувати відповідник за допомогою RSpec::Matchers.define_negated_matcher. Отже, додайте

RSpec::Matchers.define_negated_matcher :not_change, :change

до верхньої частини вашого файлу (або до вашого rails_helper.rb), а потім ви можете створити ланцюжок, використовуючи and:

expect{described_class.reorder}.to not_change{ruleset.reload.position}.
    and not_change{simple_ruleset.reload.position}

2

Прийнята відповідь не на 100% правильна, оскільки в RSpec версії 3.1.0 була додана повна підтримка складеного збігу для . Якщо ви спробуєте запустити код, наведений у прийнятій відповіді, з RSpec версії 3.0, ви отримаєте помилку.change {}

Для того, щоб використовувати складні збігаються з change {}, є два шляхи;

  • По-перше, ви повинні мати принаймні RSpec версії 3.1.0 .
  • Другий - вам потрібно додати def supports_block_expectations?; true; endдо RSpec::Matchers::BuiltIn::Compoundкласу, або мавпою, виправивши його, або безпосередньо відредагувавши локальну копію самоцвіту. Важливе зауваження: цей спосіб не повністю еквівалентний першому, expect {}блок виконується кілька разів таким чином!

Запит на витягування, який додав повну підтримку функціональних збігів, можна знайти тут .


2

Відповідь BroiSatse є найкращою, але якщо ви використовуєте RSpec 2 (або маєте більш складні збіги, як .should_not), цей метод також працює:

lambda {
  lambda {
    lambda {
      lambda {
        click_button 'Save'
        @user.reload
      }.should change {@user.name}.from('donald').to('gustav')
    }.should change {@user.updated_at}.by(4)
  }.should change {@user.great_field}.by_at_least(23)
}.should change {@user.encrypted_password}

1
Ах, гарна ідея! Можливо, ви могли б створити обгортку, щоб її було трохи легше читати!
BroiSatse
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.