RSpec: Birden çok şeyi değiştirmeyi bekleyin


86

Bir özellik spesifikasyonunda bir form gönderirken modeldeki birçok değişikliği kontrol etmek istiyorum. Örneğin, kullanıcı adının X'ten Y'ye değiştirildiğinden ve şifrelenmiş parolanın herhangi bir değerle değiştirildiğinden emin olmak istiyorum.

Bununla ilgili bazı sorular olduğunu biliyorum, ama benim için uygun bir cevap bulamadım. En doğru cevap ChangeMultiple, Michael Johnston'un eşleştiricisine benziyor burada: RSpec'in iki tabloda değişiklik beklemesi mümkün mü? . Bunun dezavantajı, yalnızca bilinen değerlerden bilinen değerlere açık değişiklikleri kontrol etmesidir.

Daha iyi bir eşleştiricinin nasıl görünebileceğine dair bazı sözde kodlar oluşturdum:

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
)

ChangeMultipleEşleştiricinin temel iskeletini de şu şekilde oluşturdum :

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

Ama şimdi zaten bu hatayı alıyorum:

 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'

Bu özel eşleştiriciyi oluştururken herhangi bir yardım çok takdir edilmektedir.

Yanıtlar:


190

RSpec 3'te aynı anda birden fazla koşulu ayarlayabilirsiniz (böylece tek beklenti kuralı bozulmaz). Şuna benzeyecektir:

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 }

Yine de tam bir çözüm değil - araştırmalarıma göre and_nothenüz yapmanın kolay bir yolu yok. Son çekinizden de emin değilim (önemli değilse, neden test edeyim?). Doğal olarak, onu özel eşleştiricinize sarabilmelisiniz .


6
Birden şeyler olduğunu beklemek istiyorum değil değişti, sadece kullanım.and change { @something }.by(0)
stevenspiel

1
Tüm parantezlerle ikinci bir örnek ekleyebilir misiniz? Hangi yöntemlerin zincirlendiğini anlamak zor
Cyril Duchon-Doris

Benim cevabım Ruby 2 için çalışıyor ve çalışmak gibi görünüyor .should_notihtiyacı herkes için
Zack Morris

39

Birden fazla kaydın değiştirilmediğini test etmek istiyorsanız, kullanarak bir eşleştiriciyi ters çevirebilirsiniz RSpec::Matchers.define_negated_matcher. Öyleyse ekle

RSpec::Matchers.define_negated_matcher :not_change, :change

dosyanızın en üstüne (veya sizin rails_helper.rb) ve ardından aşağıdakileri kullanarak zincirleyebilirsiniz and:

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

2

RSpec 3.1.0 sürümüne tam bileşik eşleme desteği change {}eklendiğinden, kabul edilen cevap% 100 doğru değil . Kabul edilen yanıtta verilen kodu RSpec sürüm 3.0 ile çalıştırmayı denerseniz, bir hata alırsınız.

Bileşik eşleştiricileri kullanmanın change {}iki yolu vardır;

  • Birincisi, en az RSpec 3.1.0 sürümüne sahip olmanız gerekir .
  • İkincisi Eklemek zorunda olduğu def supports_block_expectations?; true; endiçine RSpec::Matchers::BuiltIn::Compoundya da yama veya doğrudan mücevher yerel bir kopyasını düzenleyerek maymun tarafından, sınıf. Önemli bir not: bu yol birincisine tamamen eşdeğer değildir, expect {}blok bu şekilde birden çok kez çalışır!

Bileşik eşleştiriciler işlevselliğinin tam desteğini ekleyen çekme isteği burada bulunabilir .


2

BroiSatse'nin cevabı en iyisidir, ancak RSpec 2 kullanıyorsanız (veya daha karmaşık eşleştiricileriniz varsa .should_not), bu yöntem de işe yarar :

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}

1
Ah, güzel fikir! Okumayı biraz daha kolaylaştırmak için muhtemelen bir paketleyici oluşturabilirsiniz!
BroiSatse
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.