RSpec: Erwarten Sie, mehrere zu ändern

86

Ich möchte beim Senden eines Formulars in einer Feature-Spezifikation nach vielen Änderungen in einem Modell suchen. Ich möchte beispielsweise sicherstellen, dass der Benutzername von X in Y geändert wurde und dass das verschlüsselte Kennwort um einen beliebigen Wert geändert wurde.

Ich weiß, dass es dazu bereits einige Fragen gibt, aber ich habe keine passende Antwort für mich gefunden. Die genaueste Antwort scheint hier der ChangeMultipleMatcher von Michael Johnston zu sein: Kann RSpec Änderungen in zwei Tabellen erwarten? . Sein Nachteil ist, dass man nur nach expliziten Änderungen von bekannten Werten zu bekannten Werten sucht.

Ich habe einen Pseudocode erstellt, wie ein besserer Matcher aussehen könnte:

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
)

Ich habe auch das Grundgerüst des ChangeMultipleMatchers wie folgt erstellt:

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

Aber jetzt bekomme ich schon diesen Fehler:

 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'

Jede Hilfe bei der Erstellung dieses benutzerdefinierten Matchers wird sehr geschätzt.

Joshua Muheim
quelle

Antworten:

183

In RSpec 3 können Sie mehrere Bedingungen gleichzeitig einrichten (damit die Regel für einzelne Erwartungen nicht verletzt wird). Es würde etw aussehen wie:

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 }

Es ist jedoch keine vollständige Lösung - für meine Forschung gibt es noch keinen einfachen Weg and_not. Ich bin mir auch bei Ihrer letzten Überprüfung nicht sicher (wenn es keine Rolle spielt, warum sollten Sie sie testen?). Natürlich sollten Sie es in Ihren benutzerdefinierten Matcher einwickeln können .

BroiSatse
quelle
6
Wenn Sie erwarten möchten, dass mehrere Dinge nicht geändert werden, verwenden Sie einfach.and change { @something }.by(0)
Stevenspiel
1
Können Sie ein zweites Beispiel mit allen Klammern hinzufügen? Ich bin schwer zu verstehen, welche Methoden verkettet sind
Cyril Duchon-Doris
Meine Antwort funktioniert für Ruby 2 und scheint .should_notfür jeden zu funktionieren , der sie braucht
Zack Morris
37

Wenn Sie testen möchten, dass mehrere Datensätze nicht geändert wurden, können Sie einen Matcher mithilfe von invertieren RSpec::Matchers.define_negated_matcher. Also, füge hinzu

RSpec::Matchers.define_negated_matcher :not_change, :change

an den Anfang Ihrer Datei (oder an Ihre rails_helper.rb) und dann können Sie verketten mit and:

expect{described_class.reorder}.to not_change{ruleset.reload.position}.
    and not_change{simple_ruleset.reload.position}
Matthew Hinea
quelle
2

Die akzeptierte Antwort ist nicht 100% korrekt, da die vollständige Unterstützung für Compound Matcher für change {}in RSpec Version 3.1.0 hinzugefügt wurde . Wenn Sie versuchen, den in der akzeptierten Antwort angegebenen Code mit der RSpec-Version 3.0 auszuführen, wird eine Fehlermeldung angezeigt.

Um zusammengesetzte Matcher mit zu verwenden change {}, gibt es zwei Möglichkeiten;

  • Zunächst muss mindestens RSpec Version 3.1.0 vorhanden sein .
  • Zweitens müssen Sie def supports_block_expectations?; true; endder RSpec::Matchers::BuiltIn::CompoundKlasse etwas hinzufügen , indem Sie sie entweder mit einem Affen patchen oder die lokale Kopie des Edelsteins direkt bearbeiten. Ein wichtiger Hinweis: Dieser Weg entspricht nicht vollständig dem ersten, der expect {}Block läuft auf diese Weise mehrmals!

Die Pull-Anfrage, die die volle Unterstützung der Compound-Matcher-Funktionalität bietet, finden Sie hier .

Foo Bar Zoo
quelle
2

Die Antwort von BroiSatse ist die beste, aber wenn Sie RSpec 2 verwenden (oder komplexere Matcher wie haben .should_not), funktioniert diese Methode auch:

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}
Zack Morris
quelle
1
Ah, schöne Idee! Sie könnten wahrscheinlich einen Wrapper erstellen, um das Lesen zu erleichtern!
BroiSatse