ruby on rails - test - RSpec: Espere cambiar mĂșltiples
shoulda matchers (4)
Quiero verificar muchos cambios en un modelo al enviar un formulario en una especificación de características. Por ejemplo, quiero asegurarme de que el nombre de usuario se cambió de X a Y, y que la contraseña cifrada se cambió por cualquier valor.
Sé que ya hay algunas preguntas al respecto, pero no encontré una respuesta adecuada para mí.
La respuesta más precisa parece ser el
ChangeMultiple
ChangeMultiple de Michael Johnston aquí:
¿Es posible que RSpec espere un cambio en dos tablas?
.
Su inconveniente es que solo se verifican los cambios explícitos de valores conocidos a valores conocidos.
Creé un pseudocódigo sobre cómo creo que podría ser un mejor emparejador:
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
)
También he creado el esqueleto básico del
ChangeMultiple
ChangeMultiple de esta manera:
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
Pero ahora ya estoy recibiendo este error:
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''
Cualquier ayuda para crear este emparejador personalizado es muy apreciada.
En RSpec 3, puede configurar varias condiciones a la vez (para que la regla de expectativa única no se rompa). Se vería algo así como:
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 }
Sin embargo, no es una solución completa: en lo que respecta a mi investigación, no hay una manera fácil de hacerlo
and_not
todavía no.
Tampoco estoy seguro de su último cheque (si no importa, ¿por qué probarlo?).
Naturalmente, debería poder envolverlo dentro de su
emparejador personalizado
.
La respuesta aceptada no es 100% correcta, ya que se ha agregado el soporte
completo de
comparación de compuestos para el
change {}
en
RSpec versión 3.1.0
.
Si intenta ejecutar el código proporcionado en la respuesta aceptada con la versión 3.0 de RSpec, obtendrá un error.
Para utilizar los matchers compuestos con
change {}
, hay dos formas;
- El primero es que debe tener al menos RSpec versión 3.1.0 .
-
El segundo es, ¿tiene que agregar
def supports_block_expectations?; true; end
def supports_block_expectations?; true; end
def supports_block_expectations?; true; end
en laRSpec::Matchers::BuiltIn::Compound
, ya seaRSpec::Matchers::BuiltIn::Compound
o editando directamente la copia local de la gema. Una nota importante: esta forma no es completamente equivalente a la primera, ¡el bloqueexpect {}
se ejecuta varias veces de esta manera!
Aquí puede encontrar la solicitud de extracción que agregó el soporte completo de la funcionalidad de comparadores compuestos.
Si desea probar que no se cambiaron varios registros, puede invertir un emparejador utilizando
RSpec::Matchers.define_negated_matcher
.
Entonces, agregue
RSpec::Matchers.define_negated_matcher :not_change, :change
en la parte superior de su archivo (o en su
rails_helper.rb
) y luego puede encadenar usando
and
:
expect{described_class.reorder}.to not_change{ruleset.reload.position}.
and not_change{simple_ruleset.reload.position}
La respuesta de BroiSatse
es la mejor, pero si está utilizando RSpec 2 (o tiene coincidencias más complejas como
.should_not
), este método también funciona:
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}