ใน TDD ฉันต้องเขียน Test ก่อนหรือ Interface ก่อน?


23

ฉันกำลังเรียนรู้ TDD โดยใช้ c # เท่าที่ฉันรู้ว่าการทดสอบควรผลักดันการพัฒนานั่นคือการเขียนการทดสอบที่ล้มเหลวครั้งแรกหลังจากที่เขียนรหัสขั้นต่ำที่เปลือยเปล่าเพื่อผ่านการทดสอบแล้วทำการทดสอบใหม่

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

  1. รหัสที่ถูกเขียนขึ้นสำหรับอินเตอร์เฟซจะไม่ได้แรงหนุนจากการทดสอบ

  2. มันไม่ได้เป็นขั้นต่ำเปล่าแน่นอนฉันสามารถเขียนด้วยชั้นเรียนที่เรียบง่าย

ฉันควรเริ่มด้วยการเขียนการทดสอบสำหรับอินเทอร์เฟซด้วยหรือไม่ ฉันจะทำการทดสอบโดยไม่ใช้อะไร

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


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

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

2
TDD กล่าวว่า "เขียนการทดสอบที่ล้มเหลว" TDDers ที่เข้มงวดบางคนบอกว่ามันนับว่าเป็น "ล้มเหลว" ถ้าคุณไม่สามารถรวบรวมการทดสอบได้เนื่องจากยังไม่มีการประกาศประเภทข้อมูลที่มันใช้งานอยู่
โซโลมอนช้า

คำตอบ:


29

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

public class CalculatorTest {
    @Test
    public void testAddTwoIntegers() {
        Calculator calc = new Calculator();
        int result = calc.add(2, 2)
        Assert.assertEquals(4, result);
    }
}

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

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

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


2
+1 " ฉันไม่คิดว่าคุณสามารถแยกการออกแบบอินเตอร์เฟสออกจากการออกแบบการทดสอบ " ควรเป็นตัวหนา IMHO :)
Binary Worrier

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

คุณบอกว่าการทดสอบไม่จำเป็นต้องเปลี่ยน แต่new Calculator()การใช้งานถูกต้องหรือไม่ หากจำเป็นต้องมีการติดตั้งใหม่คุณอาจจะใช้ MultiplicationCalculator และคุณจะต้องเปลี่ยนการทดสอบเพื่อnew AdditionCalculator()ให้มันผ่านไปได้หรือไม่ หรือฉันกำลังพลาดอะไรอยู่?
Steve Chamaillard

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

5

เรากำลังทำอะไรเมื่อเราเขียนinterface? เรากำลังเขียนโค้ดหรือกำลังออกแบบอยู่หรือไม่?

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

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


4

ใน TDD ฉันต้องเขียน Test ก่อนหรือ Interface ก่อน?

ทุกอย่างขึ้นอยู่กับว่าออร์โธดอกซ์ / ศาสนาคุณต้องการทำTDDอย่างไร

ฉันกำลังเรียนรู้ TDD

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

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

  2. นอกจาก TDD หรือไม่: คำถามว่าจะใช้อินเตอร์เฟสหรือไม่ไม่น่าสนใจตั้งแต่แรก แน่นอนถ้าคุณแน่ใจว่าคุณมีพฤติกรรมที่แตกต่างกันที่คุณต้องการแพร่กระจายไปยังวัตถุต่าง ๆ มันทำให้รู้สึกคิดเกี่ยวกับการใช้อินเทอร์เฟซ: ตัวอย่างเช่นถ้าคุณมีเอาท์พุทบางชนิดไปยังปลายทางที่แตกต่างกัน อินเตอร์เฟสWriterและมีคลาสที่แตกต่างกันสำหรับเอาต์พุต ( FileWriter , Printerเป็นต้น) แม้ว่ามันจะเป็นเรื่องธรรมดาพูดกับการเขียนไปยังอินเตอร์เฟซแต่ไม่ได้หมายความว่า: ใช้อินเตอร์เฟซสำหรับทุกอย่าง บางครั้งมันเป็นระดับหนึ่งของการอ้อมไปมาก Btw บริการเดียวกันก็เหมือนกัน แต่นั่นเป็นหัวข้อที่แตกต่าง

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


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

4
อย่างที่ฉันพูดไป ... มันขึ้นอยู่กับว่าคุณเป็นออร์โธดอกซ์อย่างไร ...
โธมัสขยะ

2

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

มันยังคงทดสอบก่อนโค้ดเพราะอินเทอร์เฟซไม่มีตรรกะที่สามารถทดสอบได้ซึ่งเป็นโครงสร้างที่คุณเขียนการทดสอบ

ฉันจะทำมันดังนี้

  1. เขียนพฤติกรรมกึ่งทางการ (รับ: เมื่อ: แล้ว :)

  2. เขียนอินเตอร์เฟส (ไปยังวิธีการห่อหุ้มพฤติกรรมโฮสต์)

  3. เขียนการทดสอบระบุ (ใส่ที่ได้รับโทรเมื่อทดสอบแล้ว)

  4. เขียน / แก้ไขคอนกรีต (คลาสที่ใช้อินเทอร์เฟซ) เพื่อผ่านการทดสอบ


0

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

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


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

@gnat ฉันเชื่อว่า Nissam นี่หมายถึงคำสำคัญ C # interfaceไม่ใช่คำว่า "อินเตอร์เฟส" ทั่วไป
RubberDuck

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

ค่อนข้างยุติธรรม @gnat ที่ไม่ชัดเจนจากความคิดเห็นของคุณ ส่วนตัวฉันเห็นด้วยกับ Nissam ที่นี่ ฉันพบว่ามันหยุดคนไม่ให้ใช้ TDD เป็นข้อแก้ตัวที่จะไม่ออกแบบเลย YMMV
RubberDuck

-2

ไม่เป็นไรที่จะเขียนอินเตอร์เฟส / รหัส / ทดสอบในเวลาเดียวกันตราบใดที่การรวมตัวกันของพวกเขาในโครงการเป็นอะตอม

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

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