การเขียนแบบทดสอบสำหรับรหัสที่มีอยู่


68

สมมติว่ามีโปรแกรมที่ค่อนข้างใหญ่ (พูด 900k SLOC ใน C #) ทุกความเห็น / บันทึกไว้อย่างละเอียดจัดระเบียบดีและทำงานได้ดี ฐานรหัสทั้งหมดเขียนขึ้นโดยนักพัฒนาอาวุโสคนเดียวซึ่งไม่ได้อยู่กับ บริษัท อีกต่อไป รหัสทั้งหมดนั้นสามารถทำการทดสอบได้เหมือนเดิมและ IoC นั้นถูกใช้ไปตลอด - ยกเว้นด้วยเหตุผลแปลก ๆ บางอย่างที่พวกเขาไม่ได้เขียนการทดสอบหน่วยใด ๆ ตอนนี้ บริษัท ของคุณต้องการแยกรหัสและต้องการเพิ่มการทดสอบหน่วยเพื่อตรวจสอบเมื่อมีการเปลี่ยนแปลงการทำงานหลักของระบบ

  • การเพิ่มการทดสอบเป็นความคิดที่ดีหรือไม่
  • ถ้าเป็นเช่นนั้นเราจะเริ่มจากสิ่งนี้ได้อย่างไร

แก้ไข

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

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

สรุปผลการศึกษา

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


10
การเพิ่มการทดสอบจะไม่ทำลายรหัสที่มีอยู่
Dan Pichelman

30
@DanPichelman คุณไม่เคยมีปัญหากับschroedinbug - "ข้อบกพร่องในการออกแบบหรือการนำไปใช้งานในโปรแกรมที่ไม่ปรากฏขึ้นจนกว่าจะมีคนอ่านแหล่งข้อมูลหรือใช้โปรแกรมในลักษณะที่ผิดปกติซึ่งสังเกตว่าไม่ควรทำงานทันที หยุดทำงานสำหรับทุกคนจนกว่าจะได้รับการแก้ไข "

8
@MichaelT ตอนนี้ที่คุณพูดถึงฉันคิดว่าฉันได้เห็นหนึ่งหรือสองของเหล่านั้น ความคิดเห็นของฉันควรอ่าน "การเพิ่มการทดสอบมักจะไม่ผิดรหัสที่มีอยู่" ขอบคุณ
Dan Pichelman

3
เขียนเฉพาะการทดสอบเกี่ยวกับสิ่งที่คุณตั้งใจจะสร้างใหม่หรือเปลี่ยนแปลง
Steven Evers

3
ตรวจสอบหนังสือ "การทำงานอย่างมีประสิทธิภาพด้วยรหัสมรดก": amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/… , Michael Feathers แนะนำให้เขียนการทดสอบก่อนที่จะแก้ไขรหัสเดิม
Skarab

คำตอบ:


68

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

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

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

โดยทั่วไปวิธีการจัดการ codebase ประเภทนี้คือการเขียนการทดสอบสำหรับรหัสใหม่และการ refactoring ของรหัสเก่าจนกว่า codebase แบบดั้งเดิมจะถูก refactored ทั้งหมด

นอกจากนี้ยังเห็น


11
แต่ถึงแม้จะไม่มี TDD ก็ตามการทดสอบหน่วยก็มีประโยชน์เมื่อทำการปรับโครงสร้างใหม่
pdr

1
หากรหัสยังทำงานได้ดีก็ไม่ใช่ปัญหา แต่สิ่งที่ดีที่สุดที่ควรทำคือทดสอบอินเทอร์เฟซกับรหัสดั้งเดิมเมื่อใดก็ตามที่คุณเขียนสิ่งที่ขึ้นอยู่กับพฤติกรรมของมัน
deworde

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

3
ข้อบกพร่องที่สำคัญของ TDD คือในขณะที่ชุดอาจทำงานให้กับนักพัฒนาที่ไม่คุ้นเคยกับฐานรหัสนี้เป็นความรู้สึกที่ผิดพลาดของการรักษาความปลอดภัย BDD ดีกว่ามากในเรื่องนี้เนื่องจากผลลัพธ์เป็นความตั้งใจของรหัสในภาษาอังกฤษแบบธรรมดา
Robbie Dee

3
เช่นเดียวกับที่กล่าวถึงการครอบคลุมโค้ด 100% ไม่ได้หมายความว่าโค้ดของคุณทำงานอย่างถูกต้อง 100% ของเวลา คุณสามารถมีรหัสทุกบรรทัดสำหรับวิธีการทดสอบ แต่เพียงเพราะมันทำงานกับค่า 1 ไม่ได้หมายความว่ามันจะรับประกันว่าจะทำงานกับค่า 2
ryanzec

35

ใช่การเพิ่มการทดสอบเป็นความคิดที่ดี

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

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

ฉันอยากจะแนะนำหนังสือของ Michael Feathers ที่ทำงานอย่างมีประสิทธิภาพด้วยรหัสมรดกเพื่อคำแนะนำที่ดีและมีรายละเอียด


10
+1 หากคุณเขียนการทดสอบตามเอกสารคุณจะค้นพบความแตกต่างระหว่างรหัสการทำงานและเอกสารและสิ่งที่มีค่า
Carl Manaster

1
อีกเหตุผลสำหรับการเพิ่มการทดสอบ: เมื่อพบข้อบกพร่องคุณสามารถเพิ่มกรณีทดสอบสำหรับการทดสอบการถดถอยในอนาคตได้อย่างง่ายดาย

กลยุทธ์นี้เป็นกลยุทธ์ที่อธิบายไว้ในหลักสูตรออนไลน์ edX CS169.2x (ฟรี) ในบทเกี่ยวกับรหัสดั้งเดิม ตามที่ครูบอก: "การสร้างความจริงพื้นฐานด้วยการทดสอบลักษณะ" ในบทที่ 9 ในหนังสือ: beta.saasbook.info/table-of-contents
FGM

21

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

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

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

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


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

8

การเพิ่มการทดสอบเป็นความคิดที่ดีหรือไม่

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

อย่างไรก็ตามหากคุณกำลังจะขยาย / บำรุงรักษาโค้ดการทดสอบหน่วยที่แท้จริงนั้นมีค่าสำหรับกระบวนการนั้น

ถ้าเป็นเช่นนั้นเราจะเริ่มจากสิ่งนี้ได้อย่างไร

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

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


7
"คุณแน่ใจหรือว่าพวกเขาไม่ได้นั่งอยู่ในโซลูชั่นที่แยกจากกัน?" เป็นคำถามที่ดีมาก ฉันหวังว่า OP จะไม่มองข้าม
Dan Pichelman

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

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

3

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

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


คุณจะเพิ่มการทดสอบที่ล้มเหลวลงในโปรแกรมที่ทำงานได้ดีหรือไม่ (OP บอกว่า)
MarkJ

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

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

@DanPichelman: เห็นด้วย แต่ไม่ควรกีดกันการเขียนการทดสอบใด ๆ เลย
เบอร์นาร์ด

หากไม่มีสิ่งอื่นใดแสดงว่ารหัสไม่ได้ถูกเขียนป้องกัน
Robbie Dee

3

ตกลงฉันจะให้ความเห็นที่ตรงกันข้าม ....

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

อย่างไรก็ตามฉันจะไม่เพิ่มการทดสอบหน่วยใด ๆ

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


2

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

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

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

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