การเยาะเย้ยใน RSpec คืออะไร
(หรือล้อเลียนโดยทั่วไป เพราะนี่ไม่ใช่แนวคิดเฉพาะของ RSpec)
ล้อเลียนคือวัตถุที่ใช้ในการทดสอบ .
คุณใช้ mocks เพื่อทดสอบการโต้ตอบระหว่างสองอ็อบเจ็กต์ แทนที่จะทดสอบค่าเอาท์พุตเหมือนอย่างที่คาดไว้ทั่วไป
ตัวอย่างเช่น :
คุณกำลังเขียน API ที่พลิกภาพ
แทนที่จะเขียนโค้ดปรับแต่งภาพของคุณเอง คุณใช้อัญมณีอย่าง mini_magick
.
คุณต้องการทดสอบการโต้ตอบระหว่างโค้ดของคุณกับการพึ่งพาภายนอก .นี้ … ดังนั้น คุณจึงเขียนแบบจำลองที่คาดว่าจะเรียกใช้วิธีการที่ถูกต้องบน ImageProcessor
ชั้นเรียน
ซึ่งหมายความว่าคุณจะไม่พลิกภาพ (การทำงานช้า) ทุกครั้งที่ทำการทดสอบ
มันทำงานอย่างไร
การเยาะเย้ยมาแทนที่วัตถุดั้งเดิม ดังนั้นจึงไม่มีการเรียกวิธีการที่แท้จริง
ถึงเวลาสำหรับตัวอย่างโค้ดแล้ว!
ตัวอย่าง RSpec จำลอง
นี่คือ ImageFlipper
ทดสอบ:
RSpec.describe "ImageFlipper" ทำ "เรียกเมธอด flip ด้วยอาร์กิวเมนต์ที่ถูกต้อง" do mock =double("mini_magick") expect(mock).to receive(:flip).with("ruby.jpg") img =ImageFlipper.new(จำลอง) img.flip("ruby.jpg") สิ้นสุด
ด้วยการทดสอบนี้ เราสามารถเขียนโค้ดของเราโดยใช้ TDD
ก่อน :
เราต้องเขียน ImageFlipper
ชั้นเรียน
ถูกใจสิ่งนี้ :
คลาส ImageFlipper def initialize (image_processor) @image_processor =image_processor endend
นอกจากนี้เรายังต้องการ flip
วิธีการ:
def flip(file_name)end
ตอนนี้เราได้รับข้อเสนอแนะนี้จาก RSpec:
Failures:1) ImageFlipper เรียกใช้เมธอด flip ด้วยอาร์กิวเมนต์ที่ถูกต้อง Failure/Error:expect(mock).to receive(:flip).with("ruby.jpg") (Double "mini_magick").flip(" ruby.jpg") ที่คาดไว้:1 ครั้งโดยมีอาร์กิวเมนต์:("ruby.jpg") ได้รับ:0 ครั้ง # ./rspec-mocks.rb:6:in `block (2 ระดับ) ใน'
นี่กำลังบอกว่า flip
เมธอดถูกเรียก 0 ครั้ง แต่คาดว่าจะเรียก 1 ครั้ง
คุณสามารถทำให้การทดสอบนี้ผ่านโดยให้สิ่งที่ต้องการ :
def flip(file_name) @image_processor.flip(file_name)end
และแล้ว เรามีการทดสอบผ่าน :
.เสร็จสิ้นใน 0.00751 วินาที (ไฟล์ใช้เวลาในการโหลด 0.11157 วินาที)1 ตัวอย่าง, 0 ความล้มเหลว
มาทบทวนกัน:เราทำอะไรลงไปบ้าง
เราได้สร้าง ImageFlipper
คลาสที่ใช้ image_processor
.
โปรเซสเซอร์นี้ตอบสนองต่อ flip
วิธีการ
เราใช้การเยาะเย้ยเพื่อทดสอบว่าวิธีนี้ถูกเรียกครั้งเดียวด้วยการโต้แย้งหรือไม่
ฉันรู้.
ตัวอย่างนี้ง่าย
แต่คุณสามารถจินตนาการถึงการใช้งาน ImageFlipper
. อย่างสมบูรณ์ ที่ตรวจสอบว่ามีไฟล์อยู่หรือไม่ หากเป็นรูปภาพที่ถูกต้อง ฯลฯ
ความแตกต่างระหว่างการเยาะเย้ยและการทดสอบค่า
ในการทดสอบปกติ คุณตรวจสอบ ค่าส่งคืนของเมธอด :
“วิธีนี้ได้ภาพที่พลิกกลับหรือไม่”
เมื่อใช้ล้อเลียน คุณกำลังทดสอบพฤติกรรม :
“เราบอกผู้อื่นถึงสิ่งที่ควรทำ ด้วยข้อมูลที่ถูกต้อง และจำนวนครั้งที่เราต้องการหรือไม่”
เยาะเย้ย vs สตับ
อีกประเด็นที่สับสนคือการเปรียบเทียบการล้อเลียนและสตับ
ต่างกันอย่างไร
- ต้นขั้วเป็นเพียงวิธีการที่มีคำตอบสำเร็จรูป ไม่สนใจพฤติกรรม
- การเยาะเย้ยคาดว่าจะเรียกเมธอด หากไม่เรียก การทดสอบจะล้มเหลว
นี่คือโครงใน RSpec :
stub =double("json")allow(stub).to receive(:response) do {"blog"=>"rubyguides.com", "rating"=>"5/5"}.to_jsonend ก่อน>
allow
วิธีการคือสิ่งที่ทำให้สิ่งนี้เป็นโครงเราอนุญาตให้วัตถุทดสอบของเรา
double("json")
เพื่อรับและตอบสนองต่อวิธีนี้ แต่ เราไม่ได้ตรวจสอบว่ามีการเรียกหรือไม่ .นั่นคือความแตกต่าง!
วิธีใช้คู่ที่ยืนยันแล้ว
ข้อเสียอย่างหนึ่งของ mocks &stubs คือคุณสามารถใช้วิธีที่ไม่มีอยู่ในโค้ดที่ใช้งานจริงได้
เพราะชื่อเมธอดเปลี่ยนไป… หรือบางทีคุณอาจพิมพ์ผิด!
พบกับคู่ที่ยืนยันแล้ว .
ดับเบิลที่ตรวจสอบแล้วสามารถใช้เป็นต้นขั้วได้ (
allow
) หรือล้อเลียน (expect
) &มันจะตรวจสอบว่ามีวิธีการที่ใช้ชื่อนี้หรือไม่ตัวอย่าง :
mock =instance_double(ImageProcessor)สิ่งนี้จะทำให้เกิดข้อผิดพลาดหากไม่มีวิธีการ:
1) ImageFlipper เรียกใช้เมธอด flip ด้วยอาร์กิวเมนต์ที่ถูกต้อง Failure/Error:expect(mock).to receive(:flip).with("ruby.jpg") คลาส ImageProcessor ไม่ได้ใช้เมธอดอินสแตนซ์:flipแต่จะทำงานได้ถูกต้องหากมีวิธีการ
จำลองการคืนค่า
กลับไปเยาะเย้ยกันเถอะ
ใน ตัวอย่าง last ที่แล้ว เรามีสิ่งนี้:
คาดหวัง(เยาะเย้ย).ที่จะได้รับ(:พลิก).กับ("ruby.jpg")เมื่อรหัสของคุณเรียก
flip
ตัวจำลองจะคืนค่าnil
.หากโค้ดคาดหวังค่าอื่นที่ไม่ใช่ศูนย์ จะส่งผลให้เกิดข้อผิดพลาด
คุณสามารถแก้ไขได้โดยสร้างผลลัพธ์จำลอง
ถูกใจสิ่งนี้ :
คาดหวัง(เยาะเย้ย).ที่จะได้รับ(:พลิก).กับ("ruby.jpg").and_return("ruby-flipped.jpg")วิธีการจำลองวิธีการอินสแตนซ์
สมมติว่าคุณมีรหัสดังนี้:
คลาส NumberGenerator def สุ่ม "A" * rand(1..10) endendวิธีนี้ยากต่อการทดสอบเนื่องจากการสุ่ม
RSpec ช่วยให้คุณเยาะเย้ยหรือ stub
rand
.ถูกใจสิ่งนี้ :
มัน "สร้างตัวเลขสุ่ม" ทำเครื่องกำเนิดไฟฟ้า =NumberGenerator.new allow(generator).to receive(:rand).and_return(5) expect(generator.random).to eq("AAAAA")endตอนนี้ :
rand
ส่งคืนค่าคงที่เพื่อให้คุณใช้ทดสอบผลลัพธ์ของวิธีการได้ตามหลักการแล้ว คุณต้องการฉีดการพึ่งพาของคุณ (
rand
ในกรณีนี้) เพื่อให้คุณสามารถควบคุมได้ การฉีดการพึ่งพาหมายความว่า คุณส่งผ่านเป็นพารามิเตอร์ ไม่มีอะไรหรูหราแต่บางครั้งก็สะดวกกว่าถ้าจะใช้วิธี stub วิธีนี้
เมื่อใดควรใช้การเยาะเย้ย
ตอนนี้สำหรับคำถามใหญ่…
คุณควรใช้การเยาะเย้ยเมื่อใด
การพัฒนาซอฟต์แวร์เป็นหัวข้อที่ซับซ้อน
แต่มีหลักเกณฑ์บางประการที่คุณสามารถปฏิบัติตามได้ :
- หากเมธอดภายใต้การทดสอบคืนค่า &มันไม่มีผลข้างเคียง (การสร้างไฟล์ การส่งคำขอ API เป็นต้น) คุณไม่จำเป็นต้องมีการจำลอง เพียงตรวจสอบมูลค่าที่ส่งคืน
- หากวิธีการทำงานกับวัตถุภายนอกและส่งคำสั่งไปยังวัตถุเหล่านั้น คุณสามารถจำลองการโต้ตอบกับวัตถุเหล่านี้ได้
- หากวิธีการร้องขอข้อมูลจากบริการภายนอก (เช่น API) คุณสามารถใช้ต้นขั้วเพื่อให้ข้อมูลนี้เพื่อวัตถุประสงค์ในการทดสอบ
คุณต้องการจองการเยาะเย้ยเพื่อโต้ตอบกับโลกภายนอก .
กล่าวอีกนัยหนึ่ง…
หลีกเลี่ยงการเยาะเย้ยชั้นเรียนของแอปพลิเคชันของคุณเอง!
ทำไม?
เพราะนั่นช่วยส่งเสริมการทดสอบของคุณร่วมกับรายละเอียดการใช้งาน &ทำให้โค้ดของคุณเปลี่ยนแปลงได้ยากขึ้น .
ข้อยกเว้นเพียงอย่างเดียวคือสำหรับคลาสที่ห่อหุ้มโค้ดของบุคคลที่สาม
ดูวิดีโอนี้เพื่อเรียนรู้เพิ่มเติม :
สรุป
คุณได้เรียนรู้เกี่ยวกับ RSpec mocks, stubs &Verified doubles!
กรุณา แชร์บทความนี้ เพื่อให้ผู้คนสามารถเพลิดเพลินและได้รับประโยชน์จากเนื้อหานี้มากขึ้น
ขอบคุณที่อ่านนะคะ 🙂