RSpec: aspettati di cambiare più file


86

Voglio verificare molti cambiamenti in un modello quando si invia un modulo in una specifica di funzionalità. Ad esempio, voglio assicurarmi che il nome utente sia stato modificato da X a Y e che la password crittografata sia stata modificata da qualsiasi valore.

So che ci sono già alcune domande al riguardo, ma non ho trovato una risposta adatta a me. La risposta più accurata sembra quella del ChangeMultiplematcher di Michael Johnston: è possibile che RSpec si aspetti un cambiamento in due tabelle? . Il suo svantaggio è che si controlla solo per cambiamenti espliciti da valori noti a valori noti.

Ho creato uno pseudo codice su come penso che potrebbe apparire un abbinatore migliore:

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
)

Ho anche creato lo scheletro di base del ChangeMultiplematcher in questo modo:

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

Ma ora sto già ricevendo questo errore:

 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'

Qualsiasi aiuto nella creazione di questo abbinamento personalizzato è molto apprezzato.

Risposte:


190

In RSpec 3 è possibile impostare più condizioni contemporaneamente (quindi la regola dell'aspettativa singola non viene infranta). Sarebbe simile a qc:

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 }

Tuttavia, non è una soluzione completa: per quanto riguarda la mia ricerca, non esiste ancora un modo semplice per farlo and_not. Inoltre non sono sicuro del tuo ultimo controllo (se non importa, perché provarlo?). Naturalmente dovresti essere in grado di avvolgerlo nel tuo abbinatore personalizzato .


6
se vuoi aspettarti che più cose non vengano cambiate, usa semplicemente.and change { @something }.by(0)
stevenspiel

1
Puoi aggiungere un secondo esempio con tutte le parentesi? È difficile capire quali metodi siano concatenati
Cyril Duchon-Doris

La mia risposta funziona per Ruby 2 e sembra funzionare con .should_notchiunque ne abbia bisogno
Zack Morris

39

Se vuoi verificare che più record non siano stati modificati, puoi invertire un matcher usando RSpec::Matchers.define_negated_matcher. Quindi aggiungi

RSpec::Matchers.define_negated_matcher :not_change, :change

all'inizio del tuo file (o al tuo rails_helper.rb) e poi puoi concatenare usando and:

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

2

La risposta accettata non è corretta al 100% poiché il supporto completo del combinatore composto per change {}è stato aggiunto nella versione 3.1.0 di RSpec . Se provi a eseguire il codice fornito nella risposta accettata con RSpec versione 3.0, riceverai un errore.

Per utilizzare gli abbinamenti composti con change {}, ci sono due modi;

  • Il primo è che devi avere almeno RSpec versione 3.1.0 .
  • Il secondo è che devi aggiungere def supports_block_expectations?; true; endalla RSpec::Matchers::BuiltIn::Compoundclasse, applicando una patch scimmia o modificando direttamente la copia locale della gemma. Una nota importante: in questo modo non è completamente equivalente al primo, il expect {}blocco viene eseguito più volte in questo modo!

La richiesta pull che ha aggiunto il pieno supporto della funzionalità di abbinamenti composti può essere trovata qui .


2

La risposta di BroiSatse è la migliore, ma se stai usando RSpec 2 (o hai abbinatori più complessi come .should_not), questo metodo funziona anche:

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
Ah, bella idea! Probabilmente potresti creare un wrapper per renderlo un po 'più facile da leggere!
BroiSatse
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.