ทำความสะอาด Rspec matcher เพื่อเปลี่ยน (Model, :count) โดย (1)

ฉันกำลังทำงานอย่างหนักเพื่อรักษาไฟล์ข้อมูลจำเพาะของฉันให้สะอาดที่สุดเท่าที่จะเป็นไปได้ การใช้อัญมณี 'shoulda' และการเขียนตัวจับคู่ที่ปรับแต่งตามรูปแบบเดียวกัน

คำถามของฉันเกี่ยวกับการสร้างตัวจับคู่แบบกำหนดเองที่จะล้อม expect{ post :create ... }.to change(Model, :count).by(1) และสามารถใช้ในกลุ่มตัวอย่างเดียวกันกับตัวจับคู่ 'shoulda' อื่น ๆ รายละเอียดด้านล่าง:

ตัวจับคู่ที่กำหนดเอง (แบบง่าย)

RSpec::Matchers.define :create_a_new do |model|
  match do |dummy|
    ::RSpec::Expectations::ExpectationTarget.new(subject).to change(model, :count).by(1)
  end
end

ตัวอย่างการทำงาน

describe 'POST create:' do
  describe '(valid params)' do
    subject { -> { post :create, model: agency_attributes } }
    it { should create_a_new(Agency) }
  end
end

งานนี้ใช้ได้ตราบใดที่ฉันใช้ subject lambda และตัวจับคู่ของฉันเป็นเพียงตัวเดียวในกลุ่มตัวอย่าง

ตัวอย่างที่ล้มเหลว

ตัวอย่างที่ล้มเหลว 1

การเพิ่มตัวอย่างเพิ่มเติมในกลุ่มเดียวกันทำให้ตัวจับคู่อื่นล้มเหลวเนื่องจากตอนนี้ subject เป็นแลมบ์ดาแทนที่จะเป็นอินสแตนซ์ของคอนโทรลเลอร์

describe 'POST create:' do
  describe '(valid params)' do
    subject { -> { post :create, model: agency_attributes } }
    it { should create_a_new(Agency) }
    it { should redirect_to(Agency.last) }
  end
end

ตัวอย่างที่ล้มเหลว 2

ตัวจับคู่ 'shoulda' ต้องการให้ฉันกำหนดบล็อก before แต่สิ่งนี้เข้ากันไม่ได้กับตัวจับคู่ที่กำหนดเองของฉัน

describe 'POST create:' do
  describe '(valid params)' do
    before { post :create, agency: agency_attributes }
    it { should create_a_new(Agency) }
    it { should redirect_to(Agency.last) }
  end
end

ผลลัพธ์ที่คาดหวัง

ฉันกำลังมองหาวิธีเขียนตัวจับคู่แบบกำหนดเองที่เหมาะกับกลุ่มตัวอย่างเดียวกันกับตัวจับคู่อื่น ๆ ซึ่งหมายความว่าตัวจับคู่แบบกำหนดเองของฉันควรใช้บล็อก before เพื่อดำเนินการควบคุม "ตัวอย่างที่ล้มเหลว #2" ด้านบนคือวิธีที่ฉัน อยากจะเขียนสเป็คของฉัน เป็นไปได้ไหม?

ขอบคุณที่อ่าน


person Benj    schedule 02.08.2013    source แหล่งที่มา


คำตอบ (1)


ฉันไม่คิดว่าจะมีวิธีใดที่คุณจะได้รับตัวอย่างที่ล้มเหลวผ่าน

นี่เป็นเพราะว่า change ต้องการแลมบ์ดาจริงๆ เนื่องจากจะต้องนับสองครั้ง (หนึ่งครั้งก่อนและอีกครั้งหลังจากการเรียก) นั่นคือเหตุผลที่ฉันมักจะไม่ใช้มัน (หรือใช้มันแยกบริบท)

สิ่งที่ฉันมักจะทำแทนที่จะใช้ count matcher คือการตรวจสอบสามสิ่ง:

  • บันทึกยังคงอยู่ ถ้าฉันกำหนดโมเดลให้กับ @model ฉันจะใช้ expect(assigns(:model)).to be_persisted
  • บันทึกเป็นตัวอย่างของโมเดลที่คาดหวัง (แม้ว่าอาจดูไม่มีประโยชน์ แต่ก็ค่อนข้างมีคำอธิบายเมื่อใช้ STI) expect(assigns(:model)).to be_a(Model).
  • ตรวจสอบบันทึกล่าสุดใน DB เหมือนกับบันทึกที่ฉันเพิ่งสร้าง `expect(มอบหมาย(:model)).to eq(Model.last)``

และนั่นคือวิธีที่ฉันมักจะทดสอบ change matcher โดยไม่ใช้มัน แน่นอนว่าตอนนี้คุณสามารถสร้างตัวจับคู่ของคุณเองได้แล้ว

RSpec::Matchers.define :create_a_new do |model|
  match do |actual|
    actual.persisted? &&
      actual.instance_of?(Participant) &&
      (Participant.last == actual)
  end
end
person Serabe    schedule 04.08.2013
comment
น่าสนใจขอบคุณสำหรับการชี้แจง ให้ฉันทดสอบสิ่งนี้สักหน่อยแล้วฉันจะกลับมาพร้อมกับคุณ - person Benj; 05.08.2013
comment
อีกสิ่งหนึ่งที่ฉันกำลังลองเมื่อเร็ว ๆ นี้คือการสรุปการกระทำ (post :create, params, session) ไว้ในวิธีการ ยังไม่แน่ใจว่าจะทำต่อหรือไม่ แต่สำหรับบางกรณี มันอาจจะคุ้มค่า - person Serabe; 05.08.2013
comment
นั่นเป็นหนึ่งในความพยายามของฉันเช่นกัน set(:action) {post :create ...} แต่ฉันไม่พบวิธีที่จะแห้งจริงๆ เพราะฉันลงเอยด้วยโค้ดส่งกลิ่นเช่น before { unless example.metadata[:skip_before]; action end } - person Benj; 05.08.2013
comment
มีวิธีที่ใช้ around hooks แต่นั่นจะบ่งบอกถึงการดำเนินการตามความคาดหวังในแต่ละตัวอย่าง และฉันไม่คิดว่ามันเป็นสิ่งที่คุณต้องการ - person Serabe; 06.08.2013
comment
ขออภัยที่สละเวลาตอบ ฉันยังไม่ได้ตรวจสอบ แต่ดูเหมือนว่าสิ่งที่ถูกต้องจะทำ ฉันจะทดสอบในภายหลังทุกครั้งที่หาเวลาได้ ขอบคุณสำหรับทิป - person Benj; 11.08.2013