1. ホーム
  2. ruby-on-rails

[解決済み] RSpec: 複数回の変更に期待

2022-11-27 23:47:47

質問

機能仕様でフォームを送信する際に、モデル内の多くの変更をチェックしたい。例えば、ユーザー名がXからYに変更されたこと、暗号化されたパスワードが任意の値で変更されたことを確認したいのです。

それについてはすでにいくつかの質問があることは知っていますが、私にぴったりの回答は見つかりませんでした。最も正確な回答は、どうやら ChangeMultiple のマッチャーは、こちらのMichael Johnstonによるものです。 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 を簡単に行う方法はまだありません。また、最後のチェックについてもよくわかりません(重要でないのなら、なぜテストするのでしょうか)。当然ながら、あなたはそれを カスタム マッチャー .