web-dev-qa-db-fra.com

RSpec: Attendez-vous à changer plusieurs

Je souhaite vérifier de nombreux changements dans un modèle lors de la soumission d'un formulaire dans une spécification de fonctionnalité. Par exemple, je veux m'assurer que le nom d'utilisateur a été changé de X à Y et que le mot de passe crypté a été modifié par n'importe quelle valeur.

Je sais qu'il y a déjà des questions à ce sujet, mais je n'ai pas trouvé de réponse appropriée pour moi. La réponse la plus précise semble être celle du correcteur ChangeMultiple de Michael Johnston: Est-il possible pour RSpec d’attendre des changements dans deux tableaux? . Son inconvénient est que l'on ne vérifie que les modifications explicites des valeurs connues aux valeurs connues.

J'ai créé un pseudo-code sur la façon dont je pense qu'un meilleur matcher pourrait ressembler à ceci:

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
)

J'ai aussi créé le squelette de base du matcher ChangeMultiple comme ceci:

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

Mais maintenant, je reçois déjà cette erreur:

 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'

Toute aide à la création de cet adaptateur personnalisé est hautement appréciée.

51
Joshua Muheim

Dans RSpec 3, vous pouvez configurer plusieurs conditions à la fois (la règle d’attente unique n’est donc pas enfreinte). Cela ressemblerait à:

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 }

Ce n’est cependant pas une solution complète. En ce qui concerne mes recherches, il n’existe pas encore de solution simple and_not. Je ne suis pas sûr non plus de votre dernier chèque (s'il n'a pas d'importance, pourquoi le tester?). Naturellement, vous devriez pouvoir l’envelopper dans votre custom matcher .

112
BroiSatse

Si vous souhaitez vérifier que plusieurs enregistrements n'ont pas été modifiés, vous pouvez inverser un matcher à l'aide de RSpec::Matchers.define_negated_matcher. Alors, ajoutez

RSpec::Matchers.define_negated_matcher :not_change, :change

en haut de votre fichier (ou à votre Rails_helper.rb) et vous pourrez ensuite chaîner en utilisant and:

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

La réponse acceptée n'est pas correcte à 100%, car le support de matcher composé complet pour change {} a été ajouté dans RSpec version 3.1.0. Si vous essayez d'exécuter le code donné dans la réponse acceptée avec RSpec version 3.0, vous obtiendrez une erreur.

Pour utiliser des correspondeurs composés avec change {}, il y a deux façons;

  • Le premier est que vous devez avoir au moins RSpec version 3.1.0.
  • Deuxièmement, vous devez ajouter def supports_block_expectations?; true; end à la classe RSpec::Matchers::BuiltIn::Compound, soit en le corrigeant, soit directement en modifiant la copie locale de la gemme. Une remarque importante: cette manière n'est pas complètement équivalente à la première, le bloc expect {} s'exécute plusieurs fois de cette façon!

La demande de tirage qui a ajouté le support complet de la fonctionnalité d'appariement composés peut être trouvée ici .

1
Foo Bar Zoo

La réponse de BroiSatse est la meilleure solution, mais si vous utilisez RSpec 2 (ou avez des correspondants plus complexes comme .should_not), cette méthode fonctionne également:

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}
0
Zack Morris