จะตรวจพบข้อผิดพลาดประเภทอย่างไรในขณะที่สร้าง mocks ในภาษาแบบไดนามิก


10

ปัญหาเกิดขึ้นขณะทำ TDD หลังจากผ่านการทดสอบสองสามครั้งประเภทการคืนค่าของบางคลาส / โมดูลจะเปลี่ยนไป ในภาษาการเขียนโปรแกรมแบบสแตติกถ้าวัตถุที่เยาะเย้ยก่อนหน้านี้ถูกนำมาใช้ในการทดสอบของคลาสอื่น ๆและไม่ได้รับการแก้ไขเพื่อให้สะท้อนถึงการเปลี่ยนแปลงประเภทแล้วข้อผิดพลาดในการรวบรวมจะเกิดขึ้น

อย่างไรก็ตามสำหรับภาษาแบบไดนามิกการเปลี่ยนแปลงประเภทการส่งคืนอาจไม่ถูกตรวจพบและการทดสอบของคลาสอื่นจะยังคงผ่าน แน่ใจว่าอาจมีการทดสอบการรวมที่จะล้มเหลวในภายหลัง แต่การทดสอบหน่วยจะผ่านอย่างผิดพลาด มีวิธีใดบ้างที่จะหลีกเลี่ยงปัญหานี้?

การอัปเดตด้วยตัวอย่างเล็ก ๆ น้อย ๆ (ในบางภาษาที่สร้างขึ้น) ...

รุ่น 1:

Calc = {
    doMultiply(x, y) {return x * y}
}
//.... more code ....

// On some faraway remote code on a different file
Rect = {
    computeArea(l, w) {return Calc.doMultipy(x*y)}
}

// test for Rect
testComputeArea() { 
    Calc = new Mock()
    Calc.expect(doMultiply, 2, 30) // where 2 is the arity
    assertEqual(30, computeArea)
}

ตอนนี้ในรุ่น 2:

// I change the return types. I also update the tests for Calc
Calc = {
    doMultiply(x, y) {return {result: (x * y), success:true}}
}

... Rect จะทำให้เกิดข้อยกเว้นใน runtime แต่การทดสอบจะยังคงสำเร็จ


1
สิ่งที่คำตอบที่ดูเหมือนจะพลาดคือคำถามนั้นไม่ได้เกี่ยวกับการทดสอบที่เกี่ยวข้องกับการเปลี่ยนแปลงclass Xแต่การทดสอบclass Yนั้นขึ้นอยู่กับXและได้รับการทดสอบกับสัญญาที่แตกต่างจากที่ใช้ในการผลิต
Bart van Ingen Schenau

ฉันเพิ่งถามคำถามนี้เกี่ยวกับ SOตัวฉันเองในเรื่องการพึ่งพาการฉีด ดูเหตุผลที่ 1: ชั้นขึ้นอยู่กับสามารถเปลี่ยนแปลงได้ตลอดเวลาทำงาน (คิดว่าการทดสอบ) เราทั้งคู่มีความคิดเดียวกัน แต่ขาดคำอธิบายที่ดี
Scott Coates

ฉันกำลังอ่านคำถามของคุณอีกครั้ง แต่สับสนเล็กน้อยในการตีความ คุณสามารถให้ตัวอย่างได้หรือไม่?
Scott Coates

คำตอบ:


2

ในระดับหนึ่งนี่เป็นเพียงส่วนหนึ่งของค่าใช้จ่ายในการทำธุรกิจด้วยภาษาแบบไดนามิก คุณจะได้รับความยืดหยุ่นมากมายหรือที่รู้จักกันในชื่อ "เชือกที่เพียงพอสำหรับการแขวนตัวเอง" ระวังด้วยนะ

สำหรับฉันปัญหาแนะนำให้ใช้เทคนิคการปรับโครงสร้างที่แตกต่างจากที่คุณใช้ในภาษาที่พิมพ์แบบคงที่ ในภาษาสแตติกคุณแทนที่ชนิดส่งคืนในส่วนเพื่อให้คุณสามารถ "พึ่งพาคอมไพเลอร์" เพื่อค้นหาตำแหน่งที่อาจแตกหัก มีความปลอดภัยในการทำและอาจปลอดภัยกว่าการแก้ไขประเภทส่งคืน

ในภาษาแบบไดนามิกคุณไม่สามารถทำเช่นนั้นได้ดังนั้นจึงปลอดภัยกว่าที่จะแก้ไขประเภทส่งคืนแทนการแทนที่ อาจเป็นไปได้ว่าคุณจะปรับเปลี่ยนมันโดยการเพิ่มคลาสใหม่ของคุณเป็นสมาชิกสำหรับคลาสที่ต้องการ


2

หากรหัสของคุณเปลี่ยนแปลงและการทดสอบของคุณยังคงผ่านแสดงว่ามีบางอย่างผิดปกติกับการทดสอบของคุณ (เช่นคุณไม่มีการยืนยัน) หรือรหัสไม่เปลี่ยนแปลงจริง

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

เพื่อหลีกเลี่ยงการยืนยันที่ขาดหายไปคุณสามารถใช้เครื่องมือต่าง ๆ เช่นการวิเคราะห์ความครอบคลุมของรหัส (มันจะไม่บอกคุณว่าส่วนใดของรหัสของคุณจะถูกทดสอบ แต่มันจะบอกคุณว่าส่วนใดที่ไม่ได้ทดสอบอย่างแน่นอน) (ซึ่งทำให้คุณมีความผูกพันอยู่กับจำนวนของการยืนยันที่ต่ำกว่าจำเป็นเพื่อให้บรรลุ C1 เต็มรูปแบบและครอบคลุมรหัส C2) และการกลายพันธุ์ทดสอบ (ซึ่งกลายพันธุ์ฉีดในรหัสของคุณเช่นการเปลี่ยนtrueไปfalseตัวเลขลบเป็นบวกวัตถุลงnullฯลฯ และตรวจสอบว่าที่ ทำให้การทดสอบล้มเหลว)


+1 อาจเกิดความผิดปกติกับการทดสอบหรือรหัสไม่ได้เปลี่ยนแปลง สิ่งนี้ทำให้ฉันคุ้นเคยกับ C ++ / Java เป็นเวลาหลายปี สัญญาระหว่างส่วนต่าง ๆ ในภาษาแบบไดนามิกของรหัสไม่ควรเป็นสิ่งที่จะถูกส่งกลับ แต่สิ่งที่สิ่งที่จะถูกส่งกลับมีและสิ่งที่มันสามารถทำได้
MrFox

@suslik: อันที่จริงแล้วนี่ไม่ได้เกี่ยวกับภาษาแบบคงที่และแบบไดนามิก แต่เกี่ยวกับข้อมูลที่เป็นนามธรรมโดยใช้ประเภทข้อมูลนามธรรมและข้อมูลเชิงวัตถุที่เป็นนามธรรม ความสามารถของวัตถุหนึ่งในการจำลองวัตถุอื่นตราบเท่าที่มันไม่สามารถแยกแยะได้ (แม้ว่าวัตถุเหล่านั้นจะมีประเภทและอินสแตนซ์ที่แตกต่างกันโดยสิ้นเชิงในชั้นเรียน) เป็นพื้นฐานของแนวคิดทั้งหมดของ OO หรือกล่าวอีกนัยหนึ่ง: หากวัตถุสองชิ้นมีพฤติกรรมเหมือนกันพวกมันจะเป็นประเภทเดียวกันโดยไม่คำนึงถึงสิ่งที่ชั้นเรียนพูด ใส่อีกวิธีหนึ่ง: ชั้นเรียนไม่ได้เป็นประเภท น่าเสียดายที่ Java และ C # ทำสิ่งนี้ผิด
Jörg W Mittag

สมมติว่าหน่วยที่เยาะเย้ยนั้นได้รับการคุ้มครอง 100% และไม่มีการยืนยันที่แน่ชัด: การเปลี่ยนแปลงที่ละเอียดยิ่งขึ้นเช่น "ควรคืนค่า null สำหรับ foo" เป็น "ควรคืนค่าสตริงว่างสำหรับ foo" หากใครบางคนเปลี่ยนหน่วยและทดสอบหน่วยบริโภคอาจแตกและเนื่องจากการเยาะเย้ยนี่คือความโปร่งใส (และไม่: ในขณะที่เขียนหรือใช้โมดูลที่เยาะเย้ยตาม "สัญญา" คุณไม่จำเป็นต้องจัดการสตริงว่างเปล่าเป็นค่าส่งคืนเพราะควรส่งคืนสตริงที่ไม่ว่างเปล่าหรือโมฆะเท่านั้น)) (เฉพาะการทดสอบการรวมที่เหมาะสมของทั้งสองโมดูลในการโต้ตอบเท่านั้นที่จะสามารถจับได้)
ลองจับได้ในที่สุด
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.