การยืนยันหรือการทดสอบหน่วยสำคัญกว่าหรือไม่


51

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


4
ฉันชอบความคิดนี้มาก ... มากเพื่อให้ฉันได้รับมอบหมายหัวข้อสำหรับการทดสอบซอฟต์แวร์และหลักสูตรการประกันคุณภาพของฉัน :-)
Macneil

คำถามอื่นคือ: คุณควรทดสอบบททดสอบของคุณหรือไม่? ;)
mojuba

คำตอบ:


43

อ้างมีประโยชน์สำหรับการบอกคุณเกี่ยวกับรัฐภายในของโปรแกรม ยกตัวอย่างเช่นว่าโครงสร้างข้อมูลของคุณมีสถานะที่ถูกต้องเช่นว่าโครงสร้างข้อมูลจะไม่ถือค่าของTime 25:61:61เงื่อนไขการตรวจสอบโดยยืนยันคือ:

  • เงื่อนไขเบื้องต้นซึ่งรับประกันได้ว่าผู้โทรจะต้องรักษาสัญญาไว้

  • Postconditions ซึ่งรับประกันว่าผู้รักษาความปลอดภัยจะต้องรักษาสัญญาและ

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

การทดสอบหน่วยมีประโยชน์สำหรับการบอกคุณเกี่ยวกับพฤติกรรมภายนอกของโมดูล คุณStackอาจมีสถานะที่สอดคล้องกันหลังจากpush()เรียกใช้เมธอด แต่ถ้าขนาดของสแต็กไม่เพิ่มขึ้นสามเท่าหลังจากเรียกว่าสามครั้งนั่นเป็นข้อผิดพลาด (ตัวอย่างเช่นกรณีเล็ก ๆ น้อย ๆ ที่การpush()ใช้งานที่ไม่ถูกต้องจะตรวจสอบเฉพาะการยืนยันและออกเท่านั้น)

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

อาจมีการทับซ้อนกันบ้างในสิ่งที่กำลังทดสอบ ตัวอย่างเช่นStackpostcondition ของจริงอาจยืนยันว่าขนาดสแต็คเพิ่มขึ้นหนึ่ง แต่มีข้อ จำกัด ในสิ่งที่สามารถทำได้ในการยืนยันนั้น: มันควรตรวจสอบว่าองค์ประกอบด้านบนเป็นสิ่งที่เพิ่งเพิ่ม?

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

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

ทั้งสองมีความสำคัญและมีจุดประสงค์ของตัวเอง

[ในฐานะที่เป็นโน้ตสุดท้ายเกี่ยวกับการยืนยัน: เพื่อให้ได้ค่ามากที่สุดคุณจะต้องใช้มันในทุกจุดวิกฤติในโปรแกรมของคุณและไม่ใช่ฟังก์ชั่นหลักบางอย่าง มิฉะนั้นแหล่งที่มาดั้งเดิมของปัญหาอาจถูกปิดบังและตรวจจับได้ยากโดยไม่มีการดีบักนานหลายชั่วโมง]


7

เมื่อพูดถึงคำยืนยันโปรดจำไว้ว่าพวกเขาสามารถปิดได้ที่สะบัดของสวิตช์

ตัวอย่างการยืนยันที่แย่มาก:

char *c = malloc(1024);
assert(c != NULL);

ทำไมสิ่งนี้ถึงไม่ดี? เนื่องจากไม่มีการตรวจสอบข้อผิดพลาดหากการยืนยันนั้นถูกข้ามไปเนื่องจากมีการกำหนด NDEBUG เช่น

การทดสอบหน่วย (น่าจะ) เพียงแค่ segfault บนโค้ดด้านบน แน่นอนว่ามันทำงานด้วยการบอกคุณว่ามีบางอย่างผิดปกติหรือทำเช่นนั้น? มีโอกาสมากที่malloc()จะล้มเหลวภายใต้การทดสอบ?

การยืนยันนั้นมีวัตถุประสงค์เพื่อการดีบั๊กเมื่อโปรแกรมเมอร์ต้องการบอกเป็นนัยว่าไม่มีเหตุการณ์ 'ปกติ' ที่จะทำให้เกิดการยืนยัน malloc()ความล้มเหลวคือจริง ๆ แล้วเป็นเหตุการณ์ปกติดังนั้นจึงไม่ควรถูกกล่าวหา

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

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

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


3
นั่นเป็นเหตุผลว่าทำไมการยืนยันจึงควรเป็นข้อความจริงไม่ใช่การทดสอบข้อผิดพลาด
Dominique McDonnell

@DominicMcDonnell เอาล่ะข้อความที่ควรจะเป็นจริง บางครั้งผมยืนยันจะได้รับรอบนิสัยใจคอคอมไพเลอร์เช่นบางรุ่น GCC ที่มีเอบีเอสรถ () สร้างขึ้นใน. สิ่งสำคัญที่ต้องจำไว้คือการผลิตสร้างควรจะมีพวกเขาปิดอยู่แล้ว
ทิมโพสต์

และที่นี่ฉันคิดว่ารหัสการผลิตเป็นสถานที่ที่ต้องการการยืนยันมากที่สุดเพราะมันอยู่ในการผลิตที่คุณจะได้รับอินพุตที่คุณคิดว่าเป็นไปไม่ได้ การผลิตเป็นสถานที่สำหรับกำจัดแมลงที่ยากที่สุดออกไป
Frank Shearar

@ Frank Shearar จริงมาก คุณยังควรล้มเหลวอย่างหนักในสถานะข้อผิดพลาดที่ตรวจพบได้เร็วที่สุดในการผลิต แน่ใจว่าผู้ใช้กำลังจะบ่น แต่นั่นเป็นวิธีเดียวที่คุณจะทำให้แน่ใจว่าข้อบกพร่องจะได้รับการแก้ไข และมันก็ดีกว่ากันมากที่จะได้ blah เป็น 0 มากกว่าข้อยกเว้นหน่วยความจำสำหรับ dereferencing null บางฟังก์ชั่นการโทรในภายหลัง
Dominique McDonnell

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

5

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

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

มีโรงเรียนแห่งความคิดที่บอกว่าถ้าคุณ "ทำอะไรบางอย่าง" คุณอาจเขียนบล็อกข้อยกเว้นif/ ที่throwมีความหมาย กระบวนการคิดนี้มาจากการยืนยันจำนวนมากที่วางไว้ในจุดเริ่มต้นของวิธีการเพื่อให้แน่ใจว่าค่าทั้งหมดอยู่ในขอบเขต การทดสอบปัจจัยพื้นฐานของคุณเป็นส่วนที่สำคัญมากในการคาดคะเนความล่าช้า

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

โดยส่วนตัวแล้วฉันพบว่าฉันได้รับไมล์สะสมเพิ่มขึ้นจากการทดสอบหน่วยมากกว่าการยืนยันแบบโรย แต่นั่นเป็นเพราะแพลตฟอร์มที่ฉันพัฒนาในเกือบตลอดเวลา (Java / C #) ภาษาอื่นมีการสนับสนุนการยืนยันที่มีประสิทธิภาพมากขึ้นและแม้แต่ "Design by Contract" (ดูด้านล่าง) เพื่อรับประกันการรับประกันที่มากขึ้น ถ้าฉันทำงานกับหนึ่งในภาษาเหล่านี้ฉันอาจใช้ DBC มากกว่าการทดสอบหน่วย

http://en.wikipedia.org/wiki/Design_by_contract#Languages_with_native_support

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.