คุณได้เริ่มโครงการใหม่และถึงเวลาที่รหัสของคุณต้องพึ่งพาบริการของบุคคลที่สาม อาจเป็นบางอย่างเช่นElasticSearch, Resque, ผู้ให้บริการเรียกเก็บเงิน หรือ HTTP API โดยพลการ คุณเป็นนักพัฒนาที่ดี ดังนั้นคุณต้องการให้โค้ดนี้ได้รับการทดสอบอย่างดี แต่คุณจะทดสอบโค้ดที่ส่งคำขอไปยังบริการที่อยู่นอกเหนือการควบคุมของคุณได้อย่างไร
คุณสามารถข้ามการทดสอบได้ แต่ในไม่ช้าคุณจะซ้อนโค้ดเพิ่มเติมบนรากฐานที่สั่นคลอน โค้ดที่ยังไม่ได้ทดสอบมีแนวโน้มที่จะดึงดูดโค้ดที่ซับซ้อนมากขึ้น และในที่สุดคุณจะรู้สึกว่าโค้ดเป็นตัวสร้าง torefactor ที่อันตรายเกินไป เนื่องจากคุณไม่มีขอบเขตการทดสอบที่คุณต้องการเพื่อให้รู้สึกปลอดภัย คุณต้องการสร้างรากฐานที่มั่นคงสำหรับงานในอนาคตของคุณ แต่กลับกลายเป็นว่าคุณต้องพบกับความยุ่งเหยิงที่รักษาไม่หาย
การหลีกเลี่ยงสถานการณ์นี้ง่ายกว่ามาก! ด้วยเครื่องมือเพียงเล็กน้อยและความพยายามเพียงเล็กน้อย คุณสามารถแยกการทดสอบออกจากบริการที่โค้ดของคุณพึ่งพา เขียนโค้ดที่ง่ายกว่า และมีความมั่นใจในการปรับปรุงโค้ดที่คุณเขียนโดยไม่ต้องแนะนำบั๊ก แทนที่จะผัดวันประกันพรุ่งเพราะคุณไม่รู้ว่าจะเขียนแบบทดสอบต่อไปอย่างไร คุณสามารถดูการโต้ตอบระหว่างโค้ดของคุณกับโลกภายนอก และรู้วิธีที่จะพุ่งเข้าใส่ตรงกลางของโค้ดนั้นได้อย่างแม่นยำ
มอคค่า:วิธีที่รวดเร็วและสกปรก
มอคค่าเป็นวิธีที่ง่ายที่สุดในการเข้าร่วมระหว่างโค้ดของคุณกับโลกภายนอก
ตัวอย่างเช่น สมมติว่าคุณมี Cart
วัตถุที่เรียกการเรียกเก็บเงินจากบัตรเครดิตเมื่อได้รับการชำระเงิน คุณต้องการให้แน่ใจว่ารถเข็นมีข้อความแสดงข้อผิดพลาดแนบมาด้วยหากการเรียกเก็บเงินล้มเหลว
คุณอาจไม่ต้องการให้การทดสอบของคุณ ตี ระบบการเรียกเก็บเงินทุกครั้งที่ทำการทดสอบ แม้ว่าคุณจะทำเช่นนั้น อาจเป็นเรื่องยากที่จะบังคับให้บริการนั้นส่งคืนความล้มเหลว นี่คือสิ่งที่จะดูเหมือนกับมอคค่า:
def test_error_message_set_on_charge_failure
cart = Cart.new(items)
cart.stubs(:charge!).returns(false) # mocha in action
cart.checkout!
assert_equal "The credit card could not be charged", cart.credit_card_error
end
มอคค่าอาจล้มเหลวในการทดสอบของคุณได้เช่นกัน หากวิธีการไม่ได้ถูกเรียกในแบบที่คุณคาดหวัง:
def test_only_bill_once_per_cart
cart = Cart.new(items)
cart.expects(:charge!).once # Don't double-bill, no matter how many times we check out
cart.checkout!
cart.checkout!
end
มอคค่าใช้งานง่าย แต่มีประโยชน์อย่างเหลือเชื่อ คุณต้องระวังว่าคุณกำลังเยาะเย้ย เท่านั้น พฤติกรรมที่คุณไม่ต้องการให้เกิดขึ้น เป็นการง่ายที่จะล้อเลียนและซ่อนข้อบกพร่องที่แท้จริง คุณไม่ต้องการที่จะลงน้ำด้วยวิธีนี้:การทดสอบเต็มไปด้วยexpects
และ stubs
อ่านแล้วคิดยาก
ทดสอบของปลอม:แนวทางที่ฉันชอบ
หากคุณล้อเลียนหรือนำวิธีการเดิมๆ มาวางบนวัตถุเดียวกันตลอดเวลา คุณสามารถส่งเสริมการล้อเลียนของคุณให้เป็นวัตถุที่เต็มเปี่ยมได้ (บางครั้งเรียกว่า ทดสอบของปลอม ) ดังนี้:
def test_billed_full_amount_minus_discount
test_payment_provider = TestPaymentProvider.new # A fake payment provider
cart = Cart.new(items, discount: 30, provider: test_payment_provider)
cart.checkout!
assert_equal items.sum(:&price) * 0.7, test_payment_provider.total_charges
end
ของปลอมนั้นยอดเยี่ยม:
-
ของปลอมสามารถติดตามสถานะภายในได้
ของปลอมอาจมีข้อความยืนยันที่กำหนดเองและฟังก์ชันตัวช่วยที่ทำให้การเขียนการทดสอบของคุณง่ายขึ้น เช่น
total_charges
วิธีการตามตัวอย่างข้างต้น -
ในฐานะที่เป็นออบเจ็กต์เต็มรูปแบบ คุณจะได้รับตัวแก้ไขเพิ่มเติมและการสนับสนุนด้านภาษา
หากคุณกำลังใช้ตัวแก้ไขที่สนับสนุน คุณสามารถรับการเติมข้อความอัตโนมัติ เอกสารอินไลน์ และสิ่งอื่น ๆ ที่คุณไม่สามารถทำได้โดยใช้ Mocha อธิบายแต่ละวิธี คุณจะได้รับการตรวจสอบที่ดีขึ้น การจัดการข้อยกเว้น และสิ่งอื่น ๆ ที่คุณต้องการสร้างเป็นของปลอม
-
หากคุณใช้ของปลอมในโหมดการพัฒนา คุณไม่จำเป็นต้องเชื่อมต่อกับบริการจริง
คุณสามารถเขียนแอปของคุณบนรถบัส คุณไม่จำเป็นต้องมีบริการมากมายจนแบตเตอรี่แล็ปท็อปของคุณหมด และคุณสามารถตั้งค่าบริการปลอมเหล่านี้เพื่อส่งคืนข้อมูลที่คุณต้องการเพื่อดำเนินการผ่านเคส Edge โดยไม่ต้องใช้อะไรมาก ตั้งค่า
-
วัตถุเหล่านี้สามารถใช้นอกการทดสอบของคุณ
นี่อาจเป็นส่วนที่ฉันชอบที่สุดของของปลอม คุณสามารถมีบันทึกไคลเอ็นต์การบันทึกสำหรับทั้งบริการของบุคคลที่สามและของปลอม ซึ่งได้รับการสนับสนุนโดยอาร์เรย์ในหน่วยความจำ จากนั้นคุณสามารถดัมพ์เนื้อหาของอาร์เรย์นี้ในมุมมองผู้ดูแลระบบบนไซต์ของคุณ ทำให้ง่ายต่อการตรวจสอบว่าคุณกำลังบันทึกสิ่งที่คุณคิดว่ากำลังบันทึกอยู่
คุณสามารถทำสิ่งนี้:
fake_backend = FakeBackend.new
LoggingService.backends = [RealBackend.new, fake_backend]
LoggingService.debug("TEST MESSAGE PLEASE IGNORE")
fake_backend.messages.first # => [:debug, "TEST MESSAGE PLEASE IGNORE"]
การเขียนของปลอมนั้นต้องใช้ความพยายามมากกว่าการคัดแยกวิธีการแต่ละอย่าง แต่ด้วยการฝึกฝน ไม่ควรใช้เวลามากกว่าหนึ่งหรือสองชั่วโมงในการสร้างของปลอมที่เป็นประโยชน์ หากคุณสร้างสิ่งที่จะเป็นประโยชน์ต่อผู้อื่น แบ่งปัน! ฉันสร้างหน่วยมานานแล้ว และผู้คนจำนวนมากยังคงใช้อยู่จนถึงทุกวันนี้
ฉันจะฉีดวัตถุเหล่านี้ได้อย่างไร
คุณจะต้องทดสอบวัตถุของคุณเพื่อพูดคุยกับของปลอมเหล่านี้ โชคดีที่ Ruby ละเมิดได้ง่ายจนการฉีดของปลอมไม่ใช่เรื่องยาก
หากคุณควบคุม API ของอ็อบเจ็กต์ที่กำลังทดสอบ เป็นการดีที่สุดที่จะเพิ่มพารามิเตอร์เริ่มต้น แอตทริบิวต์ หรือตัวเลือกตัวสร้างซึ่งคุณสามารถตั้งค่าของปลอมได้:
class Card
attr_reader :provider
def initialize(items, options={})
@provider = options.fetch(:provider) { RealProvider.new }
end
end
สิ่งนี้สะอาดเมื่อคุณพูดคุยกับบริการจริงและช่วยให้คุณเพิ่มความยืดหยุ่นได้ในภายหลัง
หากคุณไม่ได้ควบคุมอ็อบเจ็กต์หรือไม่ต้องการเพิ่มพารามิเตอร์พิเศษ คุณสามารถแก้ไขลิงได้เสมอ:
# if in test mode
Card.class_eval do
def provider
@provider ||= TestProvider.new
end
end
การทดสอบนั้นน่าเกลียดกว่า แต่สะอาดกว่าในสภาพแวดล้อมที่ไม่ใช้ของปลอม
เริ่มสร้างของปลอมของคุณเองตอนนี้
การสร้างของปลอมจะง่ายขึ้นด้วยการฝึกฝน ดังนั้นคุณควรลองทำดู:
- ค้นหาการทดสอบที่พูดคุยกับบริการภายนอก การทดสอบที่จะล้มเหลวหากคุณตัดการเชื่อมต่อจากอินเทอร์เน็ตถือเป็นตัวเลือกที่ดี
- หาว่าอ็อบเจกต์ใดที่ทำการสื่อสารจริงๆ และโค้ดของคุณเรียกอะไรกับออบเจกต์นั้น
- สร้างคลาสของอ็อบเจ็กต์ที่ว่างเปล่าเป็นส่วนใหญ่ และบันทึกการโทรที่คุณทำกับอาร์เรย์
- เพิ่มวิธีการปลอมของคุณเพื่อส่งคืนรายการการโทร
- สลับของจริงกับของปลอมใหม่ของคุณ และเขียนคำยืนยันต่อต้านการโทรที่โค้ดของคุณทำ
หากคุณได้ลองใช้แล้ว แจ้งให้เราทราบด้วยว่าผลจะเป็นอย่างไร!
ด้วยเทคนิคเหล่านี้ จะใช้เวลาไม่นานจนกว่าคุณจะสามารถควบคุมการโต้ตอบที่บ้าคลั่งที่สุดระหว่างแอปพลิเคชันของคุณกับโลกภายนอกได้ ต้นขั้วง่ายๆ ในที่ที่ถูกต้องจะช่วยให้คุณจัดส่งโค้ดที่ผ่านการทดสอบมาอย่างดีด้วยความมั่นใจ