tests test run rails matchers how ruby-on-rails rspec matcher

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 la RSpec::Matchers::BuiltIn::Compound , ya sea RSpec::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 bloque expect {} 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}