Quero verificar muitas mudanças em um modelo ao enviar um formulário em uma especificação de recurso. Por exemplo, quero ter certeza de que o nome de usuário foi alterado de X para Y e que a senha criptografada foi alterada por qualquer valor.
Sei que já existem algumas perguntas sobre isso, mas não encontrei uma resposta adequada para mim. A resposta mais precisa parece ser aquela ChangeMultiple
de Michael Johnston aqui: É possível para RSpec esperar mudanças em duas tabelas? . Sua desvantagem é que só se verifica mudanças explícitas de valores conhecidos para valores conhecidos.
Criei um pseudocódigo sobre como acho que um combinador melhor poderia ser:
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
)
Também criei o esqueleto básico do ChangeMultiple
combinador como este:
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
Mas agora já estou recebendo este erro:
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'
Qualquer ajuda na criação deste matcher personalizado é muito apreciada.
fonte
.and change { @something }.by(0)
.should_not
para qualquer um que preciseSe você quiser testar se vários registros não foram alterados, você pode inverter um matcher usando
RSpec::Matchers.define_negated_matcher
. Então, adicioneRSpec::Matchers.define_negated_matcher :not_change, :change
no topo do seu arquivo (ou no seu
rails_helper.rb
) e então você pode encadear usandoand
:expect{described_class.reorder}.to not_change{ruleset.reload.position}. and not_change{simple_ruleset.reload.position}
fonte
A resposta aceita não é 100% correta, pois o suporte completo de correspondência de compostos para
change {}
foi adicionado na versão 3.1.0 do RSpec . Se você tentar executar o código fornecido na resposta aceita com o RSpec versão 3.0, receberá um erro.Para usar matchers compostos com
change {}
, existem duas maneiras;def supports_block_expectations?; true; end
àRSpec::Matchers::BuiltIn::Compound
classe, corrigindo-a ou editando diretamente a cópia local da gema. Uma nota importante: desta forma não é completamente equivalente à primeira, oexpect {}
bloco é executado várias vezes desta forma!A solicitação pull que adicionou o suporte total à funcionalidade de matchers compostos pode ser encontrada aqui .
fonte
A resposta de BroiSatse é a melhor, mas se você estiver usando RSpec 2 (ou tiver correspondências mais complexas como
.should_not
), este método também 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}
fonte