เราควรออกแบบรหัสของเราตั้งแต่ต้นเพื่อเปิดใช้การทดสอบหน่วยหรือไม่


91

มีการถกเถียงกันในทีมของเราในขณะนี้ว่าการปรับเปลี่ยนการออกแบบรหัสเพื่อให้การทดสอบหน่วยเป็นกลิ่นรหัสหรือในระดับที่สามารถทำได้โดยไม่ต้องมีกลิ่นรหัส สิ่งนี้เกิดขึ้นเพราะเราเพิ่งเริ่มวางแนวทางปฏิบัติที่มีอยู่ใน บริษัท พัฒนาซอฟต์แวร์อื่น ๆ

โดยเฉพาะเราจะมีบริการ Web API ที่จะบางมาก ความรับผิดชอบหลักของมันคือการจัดการคำขอ / ตอบกลับเว็บและเรียก API พื้นฐานที่มีตรรกะทางธุรกิจ

ตัวอย่างหนึ่งคือเราวางแผนที่จะสร้างโรงงานที่จะคืนค่าประเภทวิธีการตรวจสอบสิทธิ์ เราไม่จำเป็นต้องให้อินเทอร์เฟซสืบทอดเนื่องจากเราไม่ได้คาดหวังว่ามันจะเป็นสิ่งอื่นนอกเหนือจากรูปแบบที่เป็นรูปธรรม อย่างไรก็ตามหากต้องการทดสอบบริการ Web API เราจะต้องจำลองโรงงานนี้

นี่หมายถึงว่าเราออกแบบคลาสคอนโทรลเลอร์ API ของเว็บเพื่อยอมรับ DI (ผ่าน Constructor หรือ Setter) ซึ่งหมายความว่าเราออกแบบส่วนของคอนโทรลเลอร์เพียงเพื่อให้ DI และใช้อินเตอร์เฟสที่เราไม่ต้องการหรือเราใช้ เฟรมเวิร์กของบุคคลที่สามอย่าง Ninject เพื่อหลีกเลี่ยงการออกแบบคอนโทรลเลอร์ในลักษณะนี้ แต่เราจะต้องสร้างอินเทอร์เฟซ

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

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


33
ให้ฉันทำสิ่งนี้ซ้ำอีกครั้ง: เพื่อนร่วมงานของคุณต้องการทดสอบหน่วยสำหรับรหัสใหม่ แต่พวกเขาปฏิเสธที่จะเขียนรหัสในลักษณะที่สามารถทดสอบหน่วยได้แม้ว่าจะไม่มีความเสี่ยงในการทำลายสิ่งที่มีอยู่แล้วหรือไม่? หากเป็นจริงคุณควรยอมรับคำตอบของ @ KilianFoth และขอให้เขาเน้นประโยคแรกในคำตอบของเขาด้วยตัวหนา! เห็นได้ชัดว่าเพื่อนร่วมงานของคุณมีความเข้าใจผิดอย่างมากเกี่ยวกับงานของพวกเขา
Doc Brown

20
@ ลี: ใครบอกว่า decoupling เป็นความคิดที่ดีเสมอ คุณเคยเห็น codebase ซึ่งทุกอย่างถูกส่งผ่านเป็นส่วนต่อประสานที่สร้างขึ้นจากโรงงานส่วนต่อประสานโดยใช้ส่วนต่อประสานการกำหนดค่าบางส่วนหรือไม่ ฉันมี; มันถูกเขียนด้วยภาษาจาวาและมันก็เป็นระเบียบที่สมบูรณ์ สุดขีด decoupling เป็นรหัส obfuscation
Christian Hackl

8
การทำงานของ Michael Feathers อย่างมีประสิทธิภาพด้วย Legacy Codeเกี่ยวข้องอย่างมากกับปัญหานี้และควรให้ความคิดที่ดีเกี่ยวกับข้อดีของการทดสอบแม้ในฐานรหัสใหม่
l0b0

8
@ l0b0 นั่นเป็นข้อพระคัมภีร์สำหรับเรื่องนี้ ใน stackexchange มันไม่ได้เป็นคำตอบสำหรับคำถาม แต่ใน RL ฉันจะบอก OP เพื่อให้หนังสือเล่มนี้อ่าน (อย่างน้อยก็ส่วนหนึ่ง) OP, ได้รับการทำงานอย่างมีประสิทธิภาพกับ Legacy รหัสและอ่านมันอย่างน้อยส่วนหนึ่ง (หรือบอกเจ้านายของคุณจะได้รับมัน) มันตอบคำถามเช่นนี้ โดยเฉพาะอย่างยิ่งถ้าคุณไม่ได้ทำการทดสอบและตอนนี้คุณได้รับในมัน - คุณอาจมีประสบการณ์ 20 ปี แต่ตอนนี้คุณจะทำสิ่งที่คุณไม่ได้มีประสบการณ์กับ มันง่ายมากที่จะอ่านเกี่ยวกับพวกเขามากกว่าที่จะเรียนรู้อย่างระมัดระวังด้วยการลองผิดลองถูก
R. Schmitz

4
ขอบคุณสำหรับคำแนะนำของหนังสือของ Michael Feathers ฉันจะเก็บสำเนา
ลี

คำตอบ:


204

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

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

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

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

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


39
ฉันจะเถียงว่ามันขึ้นอยู่กับชนิดของการเปลี่ยนแปลง มีความแตกต่างระหว่างการทำโค้ดให้ง่ายต่อการทดสอบและการแนะนำ hooks เฉพาะการทดสอบที่ไม่ควรใช้ในการผลิต ฉันระวังตัวหลังเพราะเมอร์ฟี ...
แมททิวเอ็ม

61
การทดสอบหน่วยมักจะทำลายการห่อหุ้มและทำให้โค้ดภายใต้การทดสอบมีความซับซ้อนเกินกว่าที่จะต้องการเป็นอย่างอื่น (เช่นโดยการแนะนำประเภทอินเตอร์เฟสเพิ่มเติมหรือเพิ่มแฟล็ก) เช่นเดียวกับในด้านวิศวกรรมซอฟต์แวร์ทุกแนวปฏิบัติที่ดีและกฎเกณฑ์ที่ดีมีส่วนแบ่งหนี้สิน การสร้างการทดสอบหน่วยการสุ่มสี่สุ่มห้าสามารถส่งผลเสียต่อมูลค่าทางธุรกิจไม่ต้องพูดถึงว่าการเขียนและการบำรุงรักษาการทดสอบนั้นใช้เวลาและความพยายามอยู่แล้ว จากประสบการณ์ของฉันการทดสอบการรวมเข้าด้วยกันนั้นมี ROI ที่มากกว่าและมีแนวโน้มที่จะปรับปรุงสถาปัตยกรรมซอฟต์แวร์โดยมีการประนีประนอมน้อยลง
Christian Hackl

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

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

29
@ChristianHackl การทดสอบหน่วยไม่ควรทำลาย encapsulation หากคุณพยายามเข้าถึงตัวแปรส่วนตัวที่ได้รับการป้องกันหรือในระดับท้องถิ่นจากการทดสอบหน่วยคุณกำลังทำผิด หากคุณกำลังทดสอบฟังก์ชันการทำงาน foo คุณกำลังทดสอบว่าใช้งานได้จริงหรือไม่หากตัวแปรท้องถิ่น x เป็นสแควร์รูทของอินพุต y ในการวนซ้ำครั้งที่สามของลูปที่สอง ถ้าฟังก์ชั่นบางอย่างเป็นส่วนตัวแล้วไม่ว่าคุณจะกำลังทำการทดสอบทรานซิท ถ้ามันใหญ่และเป็นส่วนตัวจริงๆ นั่นเป็นข้อบกพร่องในการออกแบบ แต่อาจไม่สามารถทำได้นอก C และ C ++ ด้วยการแยกส่วนหัวใช้งาน
โอภา

75

มันไม่ง่ายอย่างที่คุณคิด มาทำลายมันกันเถอะ

  • การเขียนแบบทดสอบหน่วยเป็นสิ่งที่ดีอย่างแน่นอน

แต่!

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

  • webapi ที่ 'บางมาก' ของคุณไม่เหมือนกรณีที่ดีที่สุดสำหรับการทดสอบหน่วย

  • การเปลี่ยนรหัสและการทดสอบในเวลาเดียวกันเป็นสิ่งที่ไม่ดี

ฉันขอแนะนำวิธีต่อไปนี้:

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

  2. ตรวจสอบให้แน่ใจว่าโค้ดใหม่นั้นสามารถทดสอบได้และมีการทดสอบหน่วยและการรวมระบบ

  3. ตรวจสอบให้แน่ใจว่าเชน CI ของคุณทำการทดสอบหลังจากสร้างและปรับใช้

เมื่อคุณตั้งค่าสิ่งเหล่านั้นจากนั้นเริ่มคิดเกี่ยวกับการปรับโครงสร้างโครงการดั้งเดิมเพื่อการทดสอบ

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

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

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


12
นี้เป็นมากคำตอบที่ดีกว่าที่ได้รับการยอมรับ ความไม่สมดุลในการลงคะแนนเสียงกำลังลดลง
Konrad Rudolph

4
@Lee การทดสอบหน่วยควรทดสอบหน่วยการใช้งานซึ่งอาจหรืออาจไม่สอดคล้องกับชั้นเรียน ควรทดสอบหน่วยการใช้งานที่ส่วนต่อประสาน (ซึ่งอาจเป็น API ในกรณีนี้) การทดสอบอาจเน้นถึงกลิ่นการออกแบบและจำเป็นต้องใช้ระดับที่แตกต่างกัน / มากขึ้น สร้างระบบของคุณจากชิ้นส่วนขนาดเล็กที่ง่ายต่อการใช้เหตุผลและทดสอบ
Wes Toleman

2
@ KonradRudolph: ฉันคิดว่าพลาดจุดที่ OP เพิ่มว่าคำถามนี้เกี่ยวกับการออกแบบรหัสใหม่ไม่ใช่การเปลี่ยนรหัสเดิม ดังนั้นจึงไม่มีอะไรจะทำลายซึ่งทำให้คำตอบส่วนใหญ่ไม่สามารถใช้ได้
Doc Brown

1
ฉันไม่เห็นด้วยอย่างยิ่งกับข้อความที่เขียนการทดสอบหน่วยเป็นสิ่งที่ดีเสมอ การทดสอบหน่วยนั้นดีในบางกรณีเท่านั้น มันโง่ที่จะใช้การทดสอบหน่วยเพื่อทดสอบรหัสส่วนหน้า (UI) พวกเขาถูกสร้างขึ้นเพื่อทดสอบตรรกะทางธุรกิจ นอกจากนี้คุณควรเขียนการทดสอบหน่วยเพื่อแทนที่การตรวจสอบการรวบรวมที่ขาดหายไป (เช่นใน Javascript) รหัสส่วนหน้าเท่านั้นส่วนใหญ่ควรเขียนการทดสอบแบบครบวงจรโดยเฉพาะไม่ใช่การทดสอบหน่วย
Sulthan

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

18

การออกแบบรหัสให้ทดสอบได้โดยเนื้อแท้นั้นไม่ใช่กลิ่นรหัส ในทางตรงกันข้ามมันเป็นสัญญาณของการออกแบบที่ดี มีรูปแบบการออกแบบที่เป็นที่รู้จักและใช้กันอย่างแพร่หลายหลายรูปแบบตามรูปแบบนี้ (เช่น Model-View-Presenter) ซึ่งให้การทดสอบที่ง่าย (ง่ายกว่า) ซึ่งเป็นข้อได้เปรียบที่ยิ่งใหญ่

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

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


ปัญหานั้นสามารถแก้ไขได้ด้วยการวิเคราะห์โค้ดแบบสแตติก - ทำเครื่องหมายวิธีการ (เช่นต้องตั้งชื่อ_ForTest) และตรวจสอบรหัสฐานสำหรับการโทรจากรหัสที่ไม่ได้ทดสอบ
Riking

13

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

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

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

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


น่าสนใจที่คุณพูดถึง TDD เรากำลังพยายามที่จะนำ BDD / TDD ซึ่งได้รับการต่อต้านบางอย่าง - นั่นคือสิ่งที่ "รหัสขั้นต่ำที่จะผ่าน" หมายถึงจริงๆ
ลี

2
@ ลี: การนำการเปลี่ยนแปลงมาสู่องค์กรทำให้เกิดการต่อต้านอยู่เสมอและมันก็ต้องใช้เวลาในการปรับสิ่งใหม่ ๆ นั่นไม่ใช่ภูมิปัญญาใหม่ นี่เป็นปัญหาของคน
Doc Brown

อย่างแน่นอน ฉันแค่หวังว่าเราจะได้รับเวลามากขึ้น!
ลี

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

@ ThorbjørnRavnAndersen: ทีมอาจแสดงให้ OP เห็นเช่นกันว่าวิธีการของพวกเขาจะประหยัดเวลา ใครจะรู้? แต่ฉันสงสัยว่าถ้าเราไม่ได้เผชิญกับปัญหาทางเทคนิคน้อยกว่าที่นี่ OP มาที่นี่เพื่อบอกเราว่าทีมของเขาทำอะไรผิด (ในความคิดของเขา) ราวกับว่าเขาพยายามหาพันธมิตรเพื่อจุดประสงค์ของเขา มันอาจจะมีประโยชน์มากขึ้นจริงหารือเกี่ยวกับโครงการร่วมกันกับทีมไม่ได้อยู่กับคนแปลกหน้าบนกองแลกเปลี่ยน
Christian Hackl

11

ฉันมีปัญหากับการยืนยัน (ไม่ยืนยัน) คุณทำ:

เพื่อทดสอบหน่วยบริการ Web API เราจะต้องจำลองโรงงานนี้

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

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

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

การยืนยันที่ไม่พร้อมเพรียงอีกอย่างที่คุณทำนั้นเกี่ยวกับ DI:

เราออกแบบคลาสคอนโทรลเลอร์ API ของเว็บเพื่อยอมรับ DI (ผ่าน Constructor หรือ Setter) ซึ่งหมายความว่าเรากำลังออกแบบส่วนของคอนโทรลเลอร์เพื่อให้ DI และใช้อินเทอร์เฟซที่เราไม่ต้องการหรือเราใช้บุคคลที่สาม เฟรมเวิร์กเช่น Ninject เพื่อหลีกเลี่ยงการออกแบบตัวควบคุมในลักษณะนี้ แต่เราจะยังต้องสร้างส่วนต่อประสาน

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

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


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

9

คุณโชคดีเพราะนี่เป็นโครงการใหม่ ฉันพบว่า Test Driven Design ทำงานได้ดีมากในการเขียนโค้ดที่ดี (ซึ่งเป็นสาเหตุที่เราทำในตอนแรก)

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

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

พิจารณามัน โดยเฉพาะอย่างยิ่งกับการตรวจสอบเพื่อน จากประสบการณ์ของฉันการลงทุนเวลาและความพยายามจะกลับมาอย่างรวดเร็ว


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

@ ดู "รหัสขั้นต่ำที่จะผ่าน" ... เอาละนี่อาจฟังดูเป็นเรื่องเล็กน้อย แต่มันก็เป็นอย่างที่มันบอกไว้ เช่นถ้าคุณมีการทดสอบUserCanChangeTheirPasswordแล้วในการทดสอบที่คุณเรียกใช้ฟังก์ชั่น (ยังไม่มีอยู่) เพื่อเปลี่ยนรหัสผ่านและจากนั้นคุณยืนยันว่ารหัสผ่านนั้นมีการเปลี่ยนแปลงอย่างแน่นอน จากนั้นคุณเขียนฟังก์ชันจนกว่าคุณจะสามารถรันการทดสอบได้และจะไม่ส่งข้อยกเว้นหรือยืนยันผิด หากจุดที่คุณมีเหตุผลที่จะเพิ่มรหัสใด ๆ UserCantChangePasswordToEmptyStringแล้วเหตุผลที่จะเข้าสู่การทดสอบอื่นเช่น
R. Schmitz

@Lee ในที่สุดการทดสอบของคุณจะกลายเป็นเอกสารว่าโค้ดของคุณทำอะไรบ้างยกเว้นว่าเป็นเอกสารที่ตรวจสอบว่าเป็นจริงหรือไม่แทนที่จะเป็นแค่หมึกบนกระดาษ เปรียบเทียบกับคำถามนี้ - วิธีCalculateFactorialที่เพิ่งส่งคืน 120 และผ่านการทดสอบ นั่นคือขั้นต่ำ ก็ยังไม่ชัดสิ่งที่ตั้งใจ แต่เพียงหมายความว่าคุณต้องทดสอบอื่นที่จะแสดงสิ่งที่ถูกตั้งใจ
R. Schmitz

1
@Lee ขั้นตอนเล็ก ๆ ขั้นต่ำที่เปลือยเปล่าอาจมากกว่าที่คุณคิดเมื่อรหัสเพิ่มขึ้นเหนือสิ่งเล็กน้อย การออกแบบที่คุณทำเมื่อใช้งานทั้งหมดในคราวเดียวกันอาจไม่เหมาะสมที่สุดเพราะคุณตั้งสมมติฐานว่าควรทำอย่างไรโดยไม่ต้องเขียนข้อสอบที่แสดงให้เห็น อย่าลืมรหัสอีกครั้งในตอนแรก
Thorbjørn Ravn Andersen

1
นอกจากนี้การทดสอบการถดถอยมีความสำคัญมาก พวกเขาอยู่ในขอบเขตของทีมหรือไม่?
Thorbjørn Ravn Andersen

8

หากคุณต้องการแก้ไขรหัสนั่นคือกลิ่นรหัส

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

โค้ด Good (Clean) แบ่งงานออกเป็นส่วนย่อย ๆ ที่เข้าใจได้ง่ายในทันที (หรืออย่างน้อยก็ดูดี) การทดสอบส่วนเล็ก ๆ เหล่านี้เป็นเรื่องง่าย ฉันยังสามารถเขียนการทดสอบที่ทดสอบเฉพาะ codebase อย่างง่ายดายคล้ายกันหากฉันค่อนข้างมั่นใจเกี่ยวกับส่วนย่อย

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

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


3
"Throwaway prototype" - ทุกโปรเจ็กต์จะเริ่มต้นชีวิตเหมือนหนึ่งในนั้น ... ดีที่สุดที่จะคิดถึงสิ่งต่าง ๆ พิมพ์นี่สิฉันเดาว่าไง ... การสร้างต้นแบบต้นแบบใบปลิวขึ้นใหม่ซึ่งไม่เป็นเช่นนั้น)
Algy Taylor

4
หากคุณต้องการให้แน่ใจว่าต้นแบบที่ถูกโยนทิ้งจะถูกโยนทิ้งให้เขียนเป็นภาษาต้นแบบที่จะไม่ได้รับอนุญาตในการผลิต Clojure และ Python เป็นตัวเลือกที่ดี
Thorbjørn Ravn Andersen

2
@ ThorbjørnRavnAndersenนั่นทำให้ฉันหัวเราะหึๆ นั่นหมายถึงการขุดที่ภาษาเหล่านั้นหรือ :)
ลี

@Lee ไม่เพียงแค่ตัวอย่างของภาษาที่อาจไม่เป็นที่ยอมรับสำหรับการผลิต - โดยทั่วไปแล้วเพราะไม่มีใครในองค์กรสามารถรักษาพวกเขาเพราะพวกเขาไม่คุ้นเคยกับพวกเขาและเส้นโค้งการเรียนรู้ของพวกเขาสูงชัน หากสิ่งเหล่านั้นเป็นสิ่งที่ยอมรับได้ให้เลือกแบบอื่นที่ไม่ใช่
Thorbjørn Ravn Andersen

4

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

คุณพูด

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

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

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


2

ในสาขาวิศวกรรมทุกสาขาที่ฉันนึกออกมีเพียงหนทางเดียวที่จะบรรลุถึงระดับคุณภาพที่ดีหรือสูงกว่า:

เพื่อบัญชีสำหรับการตรวจสอบ / ทดสอบในการออกแบบ

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

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


1

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

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

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


3
คุณสามารถล่อลวงให้แนะนำเลเยอร์ทางอ้อมจำนวนมากเพื่อให้สามารถทดสอบแล้วไม่เคยใช้มันตามที่คาดไว้
Thorbjørn Ravn Andersen

1

โดยสังเขป:

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

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

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


1

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


1
"ถูกต้อง" เป็นปัญหา ฉันต้องรักษาสองโครงการที่ DI ทำผิด (แม้ว่าจะมีจุดมุ่งหมายที่จะทำ "ถูกต้อง") สิ่งนี้ทำให้โค้ดธรรมดาน่ากลัวและแย่ยิ่งกว่าโครงการดั้งเดิมที่ไม่มี DI และการทดสอบหน่วย การทำ DI ให้ถูกต้องไม่ใช่เรื่องง่าย
Jan

@Jan ที่น่าสนใจ พวกเขาทำผิดอย่างไร
ลี

1
@Lee One project เป็นบริการที่ต้องการเวลาเริ่มต้นที่รวดเร็ว แต่ช้ามากในการเริ่มต้นเนื่องจากการเริ่มต้นคลาสทั้งหมดจะทำล่วงหน้าโดย DI Framework (Castle Windsor ใน C #) ปัญหาอีกประการที่ฉันเห็นในโครงการเหล่านี้คือการผสม DI กับการสร้างวัตถุด้วย "ใหม่" ซึ่งหลีกเลี่ยง DI นั่นทำให้การทดสอบหนักขึ้นอีกครั้งและนำไปสู่สภาพการแข่งขันที่น่ารังเกียจ
Jan

1

นี่หมายถึงว่าเราออกแบบคลาสคอนโทรลเลอร์ API ของเว็บเพื่อยอมรับ DI (ผ่าน Constructor หรือ Setter) ซึ่งหมายความว่าเราออกแบบส่วนของคอนโทรลเลอร์เพียงเพื่อให้ DI และใช้อินเตอร์เฟสที่เราไม่ต้องการหรือเราใช้ เฟรมเวิร์กของบุคคลที่สามอย่าง Ninject เพื่อหลีกเลี่ยงการออกแบบคอนโทรลเลอร์ในลักษณะนี้ แต่เราจะต้องสร้างอินเทอร์เฟซ

ลองดูความแตกต่างระหว่างทดสอบได้:

public class MyController : Controller
{
    private readonly IMyDependency _thing;

    public MyController(IMyDependency thing)
    {
        _thing = thing;
    }
}

และคอนโทรลเลอร์ที่ไม่สามารถทดสอบได้:

public class MyController : Controller
{
}

ตัวเลือกเดิมมีรหัสพิเศษ 5 บรรทัดโดยสองบรรทัดนี้สามารถสร้างอัตโนมัติได้โดย Visual Studio เมื่อคุณตั้งค่ากรอบการฉีดการพึ่งพาเพื่อแทนที่ชนิดที่เป็นรูปธรรมสำหรับIMyDependencyณ รันไทม์ - ซึ่งสำหรับเฟรมเวิร์ก DI ที่ดีใด ๆ ก็เป็นอีกโค้ดหนึ่งบรรทัด - ทุกอย่างใช้งานได้ดียกเว้นตอนนี้คุณสามารถเยาะเย้ยและทดสอบตัวควบคุมของคุณ .

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

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

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


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

ตัวควบคุมมักจะมีตัวสร้างสาธารณะโดยปริยายหรือไม่เพราะ MVC ต้องการมัน "ซับซ้อน" - ถ้าคุณไม่เข้าใจว่าช่างก่อสร้างทำงานอย่างไร Encapsulation - ใช่ในบางกรณี แต่การอภิปราย DI vs encapsulation เป็นสิ่งที่ต่อเนื่องและเป็นอัตวิสัยสูงซึ่งจะไม่ช่วยที่นี่และโดยเฉพาะอย่างยิ่งสำหรับแอปพลิเคชันส่วนใหญ่ DI จะให้บริการคุณดีกว่า IMO encapsulation
Ian Kemp

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

0

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

มีแนวโน้มที่จะใช้ฟังก์ชั่นที่บริสุทธิ์มากที่สุดเท่าที่จะทำได้ (แอพที่ไม่มีเซิร์ฟเวอร์) การทดสอบหน่วยช่วยให้ฉันแยกสถานะและเขียนฟังก์ชั่นล้วน ๆ

โดยเฉพาะเราจะมีบริการ Web API ที่จะบางมาก ความรับผิดชอบหลักของมันคือการจัดการคำขอ / ตอบกลับเว็บและเรียก API พื้นฐานที่มีตรรกะทางธุรกิจ

เขียนการทดสอบหน่วยสำหรับ API พื้นฐานก่อนและถ้าคุณมีเวลาในการพัฒนาที่เพียงพอคุณต้องเขียนการทดสอบสำหรับบริการเว็บ API ที่บางด้วยเช่นกัน

TL; DR, การทดสอบหน่วยช่วยปรับปรุงคุณภาพของรหัสและช่วยให้การเปลี่ยนแปลงในอนาคตกับความเสี่ยงของรหัสฟรี นอกจากนี้ยังปรับปรุงความสามารถในการอ่านรหัส ใช้การทดสอบแทนความคิดเห็นเพื่อให้ประเด็นของคุณ


0

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

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

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

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