RSpec: ожидайте изменения нескольких
Я хочу проверить множество изменений в модели при отправке формы в спецификацию функции. Например, я хочу убедиться, что имя пользователя было изменено с 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/[email protected]/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:268:in `load'
# /Users/josh/.rvm/gems/[email protected]/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:268:in `block in load'
Любая помощь в создании этого настраиваемого соединителя высоко ценится.
Ответы
Ответ 1
В 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
. Я также не уверен в вашей последней проверке (если это не имеет значения, зачем проверять?). Естественно, вы должны иметь возможность обернуть его в свой пользовательский сопоставитель.
Ответ 2
Если вы хотите проверить, что несколько записей не были изменены, вы можете инвертировать сопоставление, используя 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}
Ответ 3
Ответ 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}
Ответ 4
Принятый ответ не на 100% правильный, поскольку в RSpec версии 3.1.0 была добавлена полная поддержка составного сопоставления для change {}
. Если вы попытаетесь запустить код, указанный в принятом ответе, с RSpec версии 3.0, вы получите сообщение об ошибке.
Чтобы использовать составные сопоставления с change {}
, есть два способа:
- Во-первых, вы должны иметь как минимум версию RSpec 3.1.0.
- Во-вторых, вы должны добавить
def supports_block_expectations?; true; end
в класс RSpec::Matchers::BuiltIn::Compound
, либо исправив его обезьяной, либо непосредственно отредактировав локальную копию драгоценного камня. Важное замечание: этот путь не полностью эквивалентен первому, блок expect {}
выполняется несколько раз таким образом!
Запрос на извлечение, в котором добавлена полная поддержка функциональности составных сопоставлений, можно найти здесь.