機能仕様でフォームを送信するときに、モデルの多くの変更を確認したい。たとえば、ユーザー名がXからYに変更され、暗号化されたパスワードが任意の値によって変更されたことを確認したい。
それについていくつかの質問があることは知っていますが、私にはふさわしい答えが見つかりませんでした。最も正確な答えは、Michael JohnstonによるChangeMultiple
matcherのようです: RSpecは2つのテーブルの変更を期待できますか? その欠点は、既知の値から既知の値への明示的な変更のみをチェックすることです。
より良いマッチャーがどのように見えるかについての疑似コードを作成しました:
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'
このカスタムマッチャーの作成にご協力いただければ幸いです。
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
まだ。また、最後のチェックについても確信が持てません(それが問題でない場合、なぜテストするのですか?)。当然、 カスタムマッチャー で囲むことができるはずです。
複数のレコードが変更されていないことをテストする場合は、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}
full複合マッチャーのchange {}
のサポートがRSpecバージョン3.1.0。 RSpecバージョン3.0で承認済みの回答に記載されているコードを実行しようとすると、エラーが発生します。
change {}
で複合マッチャーを使用するには、2つの方法があります。
def supports_block_expectations?; true; end
クラスにRSpec::Matchers::BuiltIn::Compound
を追加する必要があります。 重要な注意:この方法は最初の方法と完全に同等ではありません。expect {}
ブロックはこの方法で複数回実行されます!複合マッチャー機能の完全サポートを追加したプルリクエストは、 here にあります。
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}