หากรหัสทดสอบหน่วยของคุณ“ มีกลิ่น” มีความสำคัญหรือไม่?


52

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

ฉันควรกังวลเกี่ยวกับการทดสอบหน่วยที่ออกแบบมาไม่ดี ("เหม็น") อย่างไร


8
ยากแค่ไหนที่จะแก้ไขรหัสที่คุณคัดลอกอยู่ตลอดเวลา?
JeffO

7
รหัสกลิ่นในการทดสอบอาจเป็นตัวบ่งชี้ว่ามีกลิ่นที่ซ่อนอยู่ในส่วนที่เหลือของรหัส - ทำไมเข้าใกล้การทดสอบราวกับว่าพวกเขาไม่ใช่รหัส "ของจริง"?
HorusKol

คำตอบ:


75

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

นี่คือตัวอย่างการทดสอบกลิ่นทั่วไป:

  • ความเปราะบาง : การทดสอบของคุณล้มเหลวบ่อยครั้งและไม่คาดคิดแม้แต่กับการเปลี่ยนแปลงโค้ดที่ไม่สำคัญหรือไม่เกี่ยวข้องเลย?
  • State Leak : การทดสอบของคุณล้มเหลวแตกต่างกันไปหรือไม่ขึ้นอยู่กับว่าอะไรคือการทดสอบตามลำดับ
  • การตั้งค่า / การฉีกขาดBloat : การตั้งค่า / การฉีกขาดของคุณบล็อกยาวและยาวขึ้นหรือไม่? พวกเขาดำเนินการใด ๆ ของตรรกะทางธุรกิจหรือไม่?
  • รันไทม์ช้า : การทดสอบของคุณใช้เวลานานในการรันหรือไม่? การทดสอบหน่วยใด ๆ ของคุณใช้เวลานานกว่าสิบวินาทีในการรันหรือไม่? (ใช่ฉันจริงจังสิบนาที)
  • ความเสียดทาน : การทดสอบที่มีอยู่ทำให้ยากต่อการเขียนการทดสอบใหม่หรือไม่? คุณพบว่าตัวเองกำลังดิ้นรนกับความล้มเหลวในการทดสอบบ่อยครั้งในขณะที่ refactoring

ความสำคัญของกลิ่นคือมันเป็นตัวบ่งชี้ที่มีประโยชน์ในการออกแบบหรือปัญหาพื้นฐานอื่น ๆ เช่น "เมื่อมีควันมีไฟ" อย่าเพียงแค่มองหากลิ่นทดสอบมองหาสาเหตุที่สำคัญของพวกเขาด้วย

ในทางกลับกันนี่เป็นแนวทางปฏิบัติที่ดีสำหรับการทดสอบหน่วย:

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

2
"การทดสอบหน่วยตอบสนองวัตถุประสงค์ที่แตกต่างและมีชุดของความตึงเครียดต่าง ๆ ที่แจ้งการออกแบบของพวกเขา" ตัวอย่างเช่นควร DAMP ไม่จำเป็นต้องเป็น DRY
Jörg W Mittag

3
@ Jörgเห็นด้วย แต่ DAMP ยืนหยัดเพื่ออะไรได้จริงหรือ? : D
Rein Henrichs

5
@Rein: วลีที่สื่อความหมายและมีความหมาย ยังไม่สมบูรณ์แห้ง ดูcodeshelter.wordpress.com/2011/04/07/…
Robert Harvey

+1 ฉันก็ไม่รู้ความหมายของ DAMP
egarcia

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

67

น่าเป็นห่วง. คุณเขียนการทดสอบหน่วยเพื่อพิสูจน์รหัสของคุณตามที่คุณคาดหวัง พวกเขาอนุญาตให้คุณ refactor ได้อย่างรวดเร็วด้วยความมั่นใจ หากการทดสอบของคุณบอบบางเข้าใจยากหรือหากยากต่อการบำรุงรักษาคุณจะเพิกเฉยต่อการทดสอบที่ล้มเหลวหรือปิดการทดสอบเมื่อรหัสฐานของคุณวิวัฒนาการขึ้น


17
+1 ช่วงเวลาที่คุณเริ่มเพิกเฉยต่อการทดสอบที่ล้มเหลวซึ่งไม่ได้เพิ่มคุณค่าใด ๆ

19

ฉันเพิ่งอ่านการทดสอบศิลปะของหน่วยสองสามวันที่ผ่านมา ผู้เขียนสนับสนุนให้ใช้ความระมัดระวังในการทดสอบหน่วยการเรียนรู้ของคุณเหมือนกับรหัสการผลิตของคุณ

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


4
+1 คุณต้องทำการทดสอบเช่นเดียวกับรหัสการผลิต
Hamish Smith

อีกหนังสือที่ดีมากเกี่ยวกับหัวข้อนี้คือ "รูปแบบการทดสอบ xUnit ของ Gerard Meszaros - รหัสการทดสอบการคืนชีพ"
adrianboimvaser

7

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

Bottom line - การทดสอบมันน่าจะง่าย คุณไม่ต้องการสับสนกับรหัส "ส่งกลิ่น"


5
+1: อย่าสับสนกับรหัสทดสอบหน่วย คำถามที่โง่ที่สุดบางคำถามหมุนไปรอบ ๆ ทำให้การทดสอบหน่วย "แข็งแกร่ง" มากขึ้นเพื่อให้การเปลี่ยนแปลงการเขียนโปรแกรมไม่ทำลายการทดสอบหน่วย ความบ้า การทดสอบหน่วยถูกออกแบบมาให้เปราะ ไม่เป็นไรหากพวกเขาแข็งแกร่งน้อยกว่ารหัสแอปพลิเคชันที่ออกแบบมาเพื่อทดสอบ
S.Lott

1
@ S.Lott ทั้งสองเห็นด้วยและไม่เห็นด้วยด้วยเหตุผลอธิบายในคำตอบของฉันดีกว่า
Rein Henrichs

1
@Rein Henrichs: พวกนั้นอยู่ไกลกลิ่นที่รุนแรงกว่าที่อธิบายไว้ในคำถาม "คัดลอกและวาง" และ "ดูน่าเกลียดทีเดียว" ไม่ฟังดูแย่เท่ากลิ่นที่คุณอธิบายซึ่งการทดสอบไม่น่าเชื่อถือ
S.Lott

1
@ S.Lott แน่นอนฉันคิดว่าถ้าเราจะพูดถึง "การทดสอบหน่วยกลิ่น" ว่ามันเป็นสิ่งสำคัญที่จะทำให้ความแตกต่าง รหัสกลิ่นในการทดสอบหน่วยมักจะไม่ พวกเขาเขียนขึ้นเพื่อวัตถุประสงค์ที่แตกต่างกันและความตึงเครียดที่ทำให้พวกเขาส่งกลิ่นในบริบทหนึ่งนั้นแตกต่างกันมาก
Rein Henrichs

2
@ S.Lott BTW นี้ดูเหมือนว่าโอกาสที่ดีที่จะใช้แชทคุณลักษณะที่ถูกทอดทิ้งมาก :)
Rein Henrichs

6

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

การบำรุงรักษา

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

  • ใช้กรอบการเยาะเย้ยเพื่อให้แน่ใจว่าการทดสอบของคุณไม่ได้ขึ้นอยู่กับสิ่งภายนอก
  • ชอบต้นขั้วเหนือ mocks (ถ้ากรอบของคุณแตกต่างระหว่างพวกเขา) ทุกที่ที่เป็นไปได้
  • ไม่มีตรรกะในการทดสอบ! ifs, switch, for-eaches, cases, try-catches และอื่น ๆ ล้วนแล้วแต่เป็น no-nos ขนาดใหญ่เนื่องจากพวกมันสามารถแนะนำบั๊กในโค้ดทดสอบได้

การอ่าน

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

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

โดยสรุปคุณควรกังวลอย่างมากกับการทดสอบ "ส่งกลิ่น" ซึ่งอาจทำให้เสียเวลาโดยไม่มีค่า

คุณเคยพูดว่า:

การทดสอบหน่วยมักจะต้องมี "แฮ็กเหม็น" ต่าง ๆ เช่นฟังก์ชั่นการขัดถู

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


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

5

คำถามสองข้อสำหรับคุณ:

  • คุณคิดอย่างแน่นอนหรือไม่ว่าคุณกำลังทดสอบสิ่งที่คุณคิดว่าคุณกำลังทดสอบอยู่?
  • หากมีคนอื่นดูที่การทดสอบหน่วยพวกเขาจะสามารถทราบได้อย่างไรว่ารหัสควรจะทำอย่างไร

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

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


5

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


3

นอกจากคำตอบอื่น ๆ ที่นี่

รหัสคุณภาพต่ำในการทดสอบหน่วยไม่ได้ จำกัด อยู่ที่ชุดทดสอบหน่วยของคุณ

หนึ่งในบทบาทที่เต็มไปด้วยการทดสอบหน่วยคือเอกสาร

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

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


3

ฉันแทบไม่ได้ส่งคำตอบเพราะมันทำให้ฉันคิดว่าวิธีการนี้เป็นคำถามที่ถูกกฎหมาย

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

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

ดังนั้นคำถามที่คุณถาม (จากมุมมองของฉัน) ฉันควรประหยัดเวลาหรือเขียนโค้ดที่จะเสียเวลาหรือไม่

สำหรับคำถามที่ฉันสามารถพูดได้เท่านั้นเวลาของคุณมีความสำคัญขนาดไหน?

FYI: การขัดถูการเยาะเย้ยและการปะแก้ลิงมีการใช้ที่ถูกต้องตามกฎหมาย พวกเขาเท่านั้น "กลิ่น" เมื่อใช้งานไม่เหมาะสม


2

ฉันพยายามเป็นพิเศษเพื่อไม่ให้ทำการทดสอบหน่วยของฉันแข็งแกร่งเกินไป ฉันได้เห็นการทดสอบหน่วยที่เริ่มทำการจัดการข้อผิดพลาดในชื่อของความแข็งแกร่ง สิ่งที่คุณจบลงคือการทดสอบที่กลืนข้อบกพร่องที่พวกเขาพยายามจับ การทดสอบหน่วยยังต้องทำบางสิ่งที่น่ารักบ่อยๆเพื่อให้สิ่งต่าง ๆ ทำงานได้ คิดเกี่ยวกับความคิดทั้งหมดของ accessors ส่วนตัวโดยใช้การไตร่ตรอง ... ถ้าฉันเห็นกลุ่มคนเหล่านั้นทั้งหมดในรหัสการผลิตฉันจะเป็นห่วง 9 ครั้งจาก 10 ครั้งฉันคิดว่าควรใช้เวลามากขึ้นในการคิดเกี่ยวกับสิ่งที่ถูกทดสอบ มากกว่าความสะอาดรหัส การทดสอบจะต้องมีการเปลี่ยนแปลงบ่อยครั้งอย่างไรก็ตามถ้าคุณทำการปรับโครงสร้างครั้งสำคัญดังนั้นทำไมไม่เพียงแค่แฮ็กพวกมันเข้าด้วยกันมีความรู้สึกเป็นเจ้าของน้อยลงและมีแรงจูงใจที่จะเขียนหรือทำใหม่เมื่อถึงเวลา?


2

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

ขึ้นอยู่กับความซับซ้อนของการตั้งรหัสทดสอบอาจมีความซับซ้อนมากขึ้น (httpcontext.current กับมอนสเตอร์ที่น่ากลัวในการพยายามสร้างสิ่งปลอมปนอย่างแม่นยำ)

เว้นแต่ว่าผลิตภัณฑ์ของคุณเป็นสิ่งที่คุณไม่ค่อยเปลี่ยนการเปลี่ยนแปลงไปสู่อินเทอร์เฟซที่มีอยู่และอินพุตระดับหน่วยของคุณนั้นง่ายมากที่จะตั้งค่าฉันจะอยู่ที่ LEAST กังวลเกี่ยวกับความเข้าใจในการทดสอบเป็นผลิตภัณฑ์จริง


0

รหัสกลิ่นเป็นปัญหาในรหัสที่ทำให้ต้องมีการเปลี่ยนแปลงหรือเข้าใจในบางจุดในเวลาต่อมา

ดังนั้นฉันคิดว่าคุณควรแก้ไขรหัสกลิ่นเมื่อคุณต้อง "ปิด" เพื่อทดสอบหน่วยที่กำหนด แต่ไม่ก่อน

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