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.
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 .
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}
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;
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 .
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}