RSpec: คาดว่าจะมีการเปลี่ยนแปลงหลายรายการ


86

ฉันต้องการตรวจสอบการเปลี่ยนแปลงหลายอย่างในโมเดลเมื่อส่งแบบฟอร์มในคุณสมบัติจำเพาะ ตัวอย่างเช่นฉันต้องการตรวจสอบให้แน่ใจว่าชื่อผู้ใช้เปลี่ยนจาก X เป็น Y และรหัสผ่านที่เข้ารหัสถูกเปลี่ยนด้วยค่าใด ๆ

ฉันรู้ว่ามีคำถามบางอย่างเกี่ยวกับเรื่องนี้แล้ว แต่ฉันไม่พบคำตอบที่เหมาะสมสำหรับฉัน คำตอบที่ถูกต้องที่สุดดูเหมือนผู้ChangeMultipleจับคู่โดย Michael Johnston ที่นี่: เป็นไปได้หรือไม่ที่ RSpec จะคาดหวังการเปลี่ยนแปลงในสองตาราง? . ข้อเสียคือตรวจสอบการเปลี่ยนแปลงอย่างชัดเจนจากค่าที่ทราบเป็นค่าที่ทราบเท่านั้น

ฉันสร้างรหัสหลอกว่าฉันคิดว่าตัวจับคู่ที่ดีกว่าจะมีลักษณะอย่างไร:

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
)

ฉันได้สร้างโครงกระดูกพื้นฐานของตัวChangeMultipleจับคู่เช่นนี้:

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

แต่ตอนนี้ฉันได้รับข้อผิดพลาดนี้แล้ว:

 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'

ความช่วยเหลือใด ๆ ในการสร้างตัวจับคู่แบบกำหนดเองนี้ได้รับการชื่นชมอย่างมาก

คำตอบ:


190

ใน RSpec 3 คุณสามารถตั้งค่าหลายเงื่อนไขพร้อมกันได้ (ดังนั้นกฎความคาดหวังเดียวจะไม่เสีย) มันจะดูเหมือนว่า:

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 }

มันไม่ได้เป็นโซลูชั่นที่สมบูรณ์แม้ว่า - เท่าที่การวิจัยของฉันก็ไม่มีวิธีที่ง่ายที่จะทำand_notเลย ฉันไม่แน่ใจเกี่ยวกับการตรวจสอบครั้งล่าสุดของคุณด้วย (ถ้าไม่สำคัญทำไมต้องทดสอบ?) ธรรมชาติที่คุณควรจะสามารถห่อภายในของคุณจับคู่ที่กำหนดเอง


6
หากคุณต้องการคาดหวังว่าหลายสิ่งจะไม่เปลี่ยนแปลงเพียงใช้.and change { @something }.by(0)
stevenspiel

1
เพิ่มตัวอย่างที่ 2 พร้อมวงเล็บทั้งหมดได้ไหม ฉันยากที่จะเข้าใจว่าวิธีใดถูกล่ามโซ่
Cyril Duchon-Doris

คำตอบของฉันใช้ได้กับ Ruby 2 และดูเหมือนว่าจะใช้ได้กับ.should_notทุกคนที่ต้องการ
Zack Morris

39

RSpec::Matchers.define_negated_matcherหากคุณต้องการที่จะทดสอบว่าหลายระเบียนไม่มีการเปลี่ยนแปลงคุณสามารถสลับการจับคู่โดยใช้ ดังนั้นเพิ่ม

RSpec::Matchers.define_negated_matcher :not_change, :change

ไปที่ด้านบนสุดของไฟล์ของคุณ (หรือของคุณrails_helper.rb) จากนั้นคุณสามารถเชื่อมโยงโดยใช้and:

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

2

คำตอบที่ได้รับการยอมรับไม่ได้ถูกต้อง 100% ตั้งแต่เต็มรูปแบบสนับสนุนการจับคู่สารประกอบchange {}ได้รับการเพิ่มในRSpec เวอร์ชัน 3.1.0 หากคุณพยายามเรียกใช้รหัสที่ระบุในคำตอบที่ยอมรับด้วย RSpec เวอร์ชัน 3.0 คุณจะได้รับข้อผิดพลาด

ในการใช้ตัวจับคู่แบบผสมchange {}มีสองวิธี:

  • คนแรกคือคุณต้องมีอย่างน้อยรุ่น RSpec 3.1.0
  • อย่างที่สองคือคุณต้องเพิ่มdef supports_block_expectations?; true; endเข้าไปในRSpec::Matchers::BuiltIn::Compoundคลาสไม่ว่าจะโดยการปะลิงหรือแก้ไขสำเนาเฉพาะของอัญมณีโดยตรง หมายเหตุสำคัญ:วิธีนี้ไม่เทียบเท่ากับวิธีแรกอย่างสมบูรณ์expect {}บล็อกจะทำงานหลายครั้งด้วยวิธีนี้!

คำขอดึงซึ่งเพิ่มการสนับสนุนอย่างเต็มที่ของการทำงาน matchers สารประกอบที่สามารถพบได้ที่นี่


2

คำตอบของ BroiSatseดีที่สุด แต่ถ้าคุณใช้ RSpec 2 (หรือมีตัวจับคู่ที่ซับซ้อนกว่า.should_notนี้) วิธีนี้ก็ใช้ได้เช่นกัน:

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
อาเป็นความคิดที่ดี! คุณอาจสร้างกระดาษห่อหุ้มเพื่อให้อ่านง่ายขึ้น!
BroiSatse
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.