การทดสอบหน่วยนำไปสู่การวางนัยทั่วไปก่อนกำหนด (โดยเฉพาะในบริบทของ C ++) หรือไม่


20

บันทึกเบื้องต้น

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

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

ปัญหาการแยกตัว

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

Micheal Feathers พูดถึงแนวคิดของรอยต่อ : [WEwLC, p31]

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

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

ตัวอย่าง

การทดสอบหน่วย - โดยเฉพาะอย่างยิ่งใน C ++ ต้องการรหัสจากการทดสอบเพื่อเพิ่มตะเข็บเพิ่มเติมที่จะถูกเรียกอย่างเคร่งครัดสำหรับปัญหาที่ระบุ

ตัวอย่าง:

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

คำถาม

ฉันจะลองรุ่นไม่กี่รุ่นที่หวังว่าจะถามถึงจุดเดียวกัน:

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

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


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

@ hotpaw2 ดูหมิ่น! :)
maple_shaft

คำตอบ:


23

การทดสอบหน่วย - โดยเฉพาะอย่างยิ่งใน C ++ ต้องการรหัสจากการทดสอบเพื่อเพิ่มตะเข็บเพิ่มเติมที่จะถูกเรียกอย่างเคร่งครัดสำหรับปัญหาที่ระบุ

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

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

กลับไปสู่โลกของซอฟต์แวร์ C ++ นั้นยากต่อการทดสอบหน่วยมากกว่าภาษาที่มีการโหลดคลาสแบบไดนามิกการสะท้อนกลับและอื่น ๆ อย่างไรก็ตามปัญหาส่วนใหญ่สามารถลดลงได้อย่างน้อย ในโครงการ C ++ เดียวที่ฉันใช้การทดสอบหน่วยจนถึงตอนนี้เราไม่ได้ทำการทดสอบบ่อยเท่าที่เราต้องการในโครงการ Java - แต่ก็ยังเป็นส่วนหนึ่งของการสร้าง CI ของเราและเราพบว่ามีประโยชน์

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

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

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

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

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

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


ท่านครับผมขอแสดงความนับถือ
GordonM

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

4

ฉันจะโยนวิถีแห่ง Testivus มาที่คุณ แต่เพื่อสรุป:

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

คำแนะนำที่ง่ายที่สุดคือ: สิ่งที่คุณทำการทดสอบคือส่วนต่อประสานสาธารณะของรหัสของคุณในแบบที่มันถูกใช้โดยส่วนอื่น ๆ ของระบบ

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

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

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


ฉันจะเพิ่มเหมือนกัน: ทำ api ทดสอบ api จากด้านนอก
Christopher Mahan

2

TDD และการทดสอบหน่วยเป็นสิ่งที่ดีสำหรับโปรแกรมโดยรวมและไม่เพียง แต่สำหรับการทดสอบหน่วย เหตุผลนี้เป็นเพราะมันดีต่อสมอง

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

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


1

ทดสอบหน่วยแยกตัวที่เล็กที่สุดของแอปพลิเคชัน

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

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

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

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

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


0

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

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

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

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