จะทำอย่างไรเมื่อการทดสอบ TDD เปิดเผยการทำงานใหม่ที่จำเป็นต้องมีการทดสอบด้วย?


13

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

ตัวอย่างเช่นฉันกำลังเขียนคลาสพอยต์ที่มีฟังก์ชั่นWillCollideWith ( LineSegment ) :

public class Point {
    // Point data and constructor ...

    public bool CollidesWithLine(LineSegment lineSegment) {
        Vector PointEndOfMovement = new Vector(Position.X + Velocity.X,
                                               Position.Y + Velocity.Y);
        LineSegment pointPath = new LineSegment(Position, PointEndOfMovement);
        if (lineSegment.Intersects(pointPath)) return true;
        return false;
    }
}

ผมเขียนทดสอบสำหรับCollidesWithLineเมื่อฉันรู้ว่าฉันจะต้องมีLineSegment.Intersects ( ส่วนของเส้นตรง )ฟังก์ชั่น แต่ฉันควรหยุดสิ่งที่ฉันทำในรอบการทดสอบเพื่อสร้างฟังก์ชันใหม่นี้หรือไม่ นั่นดูเหมือนจะทำลายหลักการ "แดงเขียวเขียวรีแฟคเตอร์"

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

คำตอบ:


14

เพียงแสดงความคิดเห็นการทดสอบและรหัสล่าสุดของคุณ (หรือใส่ในที่ซ่อน) ดังนั้นในความเป็นจริงคุณจึงหันนาฬิกากลับไปที่จุดเริ่มต้นของรอบ จากนั้นเริ่มต้นด้วยการLineSegment.Intersects(LineSegment)ทดสอบ / รหัส / refactor เมื่อเสร็จแล้วให้ยกเลิกการใส่เครื่องหมายในการทดสอบ / รหัสก่อนหน้าของคุณ (หรือดึงออกจากที่เก็บข้อมูล) และทำตามรอบ


สิ่งนี้แตกต่างกันอย่างไรเพียงแค่เพิกเฉยและกลับมาใหม่ในภายหลัง
โจชัวแฮร์ริส

1
รายละเอียดเล็ก ๆ น้อย ๆ : ไม่มีการทดสอบพิเศษ "ละเว้นฉัน" ในรายงานและหากคุณใช้การหยุดทำงานรหัสจะไม่สามารถแยกออกได้จากกรณี 'สะอาด'
Javier

ที่ซ่อนคืออะไร เหมือนกับการควบคุมเวอร์ชันไหม
โจชัวแฮร์ริส

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

6

ในวงจร TDD:

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

ในโซลูชันการออกแบบปัจจุบันของคุณ

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

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

ดังนั้นความรับผิดชอบนี้ควรเป็นของคลาสอื่นเช่น "PointSegmentCollisionDetector" ซึ่งจะมีวิธีดังนี้:

บูล AreInCollision (จุด p, ส่วนของ s)

และนั่นคือสิ่งที่คุณจะทดสอบแยกต่างหากจากคะแนนและกลุ่ม

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

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

หวังว่ามันจะสมเหตุสมผล


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

2

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


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

0

ด้วย jUnit4 คุณสามารถใช้@Ignoreคำอธิบายประกอบสำหรับการทดสอบที่คุณต้องการเลื่อนออกไป

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

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