กำลังสร้างวัตถุที่คุณคิดว่าคุณต้องใช้ในการทดสอบครั้งแรกใน TDD


15

ฉันค่อนข้างใหม่สำหรับ TDD และฉันมีปัญหาในการสร้างการทดสอบครั้งแรกของฉันเมื่อมันมาก่อนรหัสการติดตั้งใด ๆ หากไม่มีกรอบในการติดตั้งโค้ดฉันมีอิสระที่จะเขียนการทดสอบครั้งแรกของฉันได้ แต่ฉันต้องการ แต่มันก็ดูจะเสียไปโดยวิธีการคิดแบบ Java / OO ของฉันเสมอ

ตัวอย่างเช่นในGithub ConwaysGameOfLifeของฉันตัวอย่างการทดสอบครั้งแรกที่ฉันเขียน (rule1_zeroNeighbours) ฉันเริ่มต้นด้วยการสร้างวัตถุ GameOfLife ที่ยังไม่ได้ใช้งาน เรียกว่าชุดวิธีการที่ไม่มีอยู่วิธีการขั้นตอนที่ไม่มีอยู่วิธีรับที่ไม่มีอยู่แล้วใช้การยืนยัน

การทดสอบพัฒนาขึ้นเมื่อฉันเขียนการทดสอบและปรับโครงสร้างใหม่ แต่เดิมแล้วมันมีลักษณะดังนี้:

@Test
public void rule1_zeroNeighbours()
{
    GameOfLife gameOfLife = new GameOfLife();
    gameOfLife.set(1, 1, true);
    gameOfLife.step();
    assertEquals(false, gameOfLife.get(1, 1));
}

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

ในวิธีที่คุณเข้าใจ TDD มันโอเคไหม? ฉันดูเหมือนจะปฏิบัติตามหลักการ TDD / XP ในการทดสอบและการใช้งานของฉันที่พัฒนาตลอดเวลาด้วยการปรับโครงสร้างใหม่และดังนั้นหากการออกแบบเริ่มต้นนี้ได้พิสูจน์แล้วว่าไร้ประโยชน์มันจะเปิดให้มีการเปลี่ยนแปลง แต่รู้สึกว่าฉันกำลังบังคับทิศทาง วิธีแก้ปัญหาโดยเริ่มต้นด้วยวิธีนี้

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


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

คำตอบ:


9

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

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

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

การออกแบบอย่างมืออาชีพ

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

การออกแบบทดสอบการขับขี่มืออาชีพ

  • โดยการสร้างการใช้งานของคุณรอบ ๆ ไคลเอนต์ของรหัสของคุณ (การทดสอบของคุณ), คุณได้รับ YAGNI - adherence สวยมากฟรีตราบใดที่คุณไม่เริ่มเขียนกรณีทดสอบที่ไม่จำเป็น โดยทั่วไปแล้วคุณจะได้รับ API ที่ออกแบบโดยผู้บริโภคซึ่งเป็นสิ่งที่คุณต้องการในที่สุด

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

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


ลิงค์นี้เป็นเบ็นที่อ่านได้ดีมาก ขอบคุณสำหรับการแบ่งปัน
RubberDuck

1
@RubberDuck ยินดีต้อนรับ! จริง ๆ แล้วฉันไม่เห็นด้วยกับมัน แต่ฉันคิดว่ามันเป็นงานที่ยอดเยี่ยมในการโต้แย้งมุมมองนั้น
Ben Aaronson

1
ฉันไม่แน่ใจว่าฉันทำอย่างใดอย่างหนึ่ง แต่มันทำให้กรณีของมันเป็นอย่างดี ฉันคิดว่าคำตอบที่ถูกต้องอยู่ตรงกลาง คุณต้องมีแผน แต่แน่นอนถ้าการทดสอบของคุณรู้สึกอึดอัดใจออกแบบใหม่ อย่างไรก็ตาม ... ++ แสดงให้ดีว่าถั่วเก่า
RubberDuck

17

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

จากการทดสอบหน่วยปฏิบัติกับ JUnit และ Mockito :

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

การทดสอบของคุณไม่ได้พยายามออกแบบ API มากนัก คุณได้ตั้งค่าระบบ stateful ที่ทุกฟังก์ชั่นที่มีอยู่ภายในนอกGameOfLifeชั้นเรียน

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

ง่ายที่จะปัดเศษขั้นตอน "เขียนการทดสอบที่ล้มเหลว" แต่การเขียนการทดสอบที่ล้มเหลวซึ่งทำงานในแบบที่คุณต้องการคือหัวใจของ TDD


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

2
@immibis นั่นคือการพูดเล่นมากกว่ารายละเอียด คุณสามารถเริ่มต้นด้วยคลาสที่แสดงคอลเลกชันของเซลล์ คุณสามารถโยกย้าย / รวมคลาสเซลล์และทดสอบกับคลาสที่แสดงคอลเลกชันของเซลล์ในภายหลังหากประสิทธิภาพเป็นปัญหา
Eric

@immibis จำนวนเพื่อนบ้านสดสามารถจัดเก็บได้ด้วยเหตุผลด้านประสิทธิภาพ จำนวนเห็บเซลล์ยังมีชีวิตอยู่เนื่องจากการระบายสี ..
Blorgbeard ออก

@Imibis การเพิ่มประสิทธิภาพก่อนวัยอันควรเป็นความชั่วร้าย ... นอกจากนี้การหลีกเลี่ยงความหลงไหลแบบดั้งเดิมเป็นวิธีที่ยอดเยี่ยมในการเขียนโค้ดคุณภาพดีไม่ว่าจะสนับสนุนกี่รัฐก็ตาม ลองดูที่: jamesshore.com/Blog/PrimitiveObsession.html
Paul

0

มีโรงเรียนคิดต่าง ๆ เกี่ยวกับเรื่องนี้

บางคนพูดว่า: การทดสอบที่ไม่ได้คอมไพล์เป็นข้อผิดพลาด - ไปแก้ไขเขียนรหัสการผลิตที่เล็กที่สุดที่มีอยู่

บางคนพูดว่า: มันก็โอเคที่จะเขียนการทดสอบก่อนตรวจสอบว่ามันมด (หรือไม่) มดแล้วสร้างชั้นเรียน / วิธีการที่ขาดหายไป

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

มันขึ้นอยู่กับคุณที่จะเลือกวิธีการทำงานของคุณ IMHO ทั้งสองวิธีนั้นถูกต้อง


0

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

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

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


0

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

TDD เป็นเพียงการเชื่อมโยงแนวทางการพัฒนา: TDD
1. เพิ่มการทดสอบ
2. เรียกใช้การทดสอบทั้งหมดและดูว่าการทดสอบใหม่ล้มเหลว
หรือไม่3. เขียนรหัสบางส่วน
4. เรียกใช้การทดสอบ
5. เรียกใช้การทดสอบ5. รหัส Refactor
6. ทำซ้ำ

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


0

ฉันไม่ชอบการทดสอบระดับระบบที่เขียนด้วยภาษาจาวาหรือ C # ด้วยเหตุผลนั้น ดู SpecFlow สำหรับ c # หรือหนึ่งในกรอบการทดสอบตาม Cucumber สำหรับ java (อาจ JBehave) จากนั้นการทดสอบของคุณจะมีลักษณะเช่นนี้มากขึ้น

ป้อนคำอธิบายรูปภาพที่นี่

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

(การทดสอบหน่วย“ ปกติ” นั้นยอดเยี่ยมเมื่อทดสอบคลาสเดียว)

อะไรคือความแตกต่างระหว่างเฟรมเวิร์ก BDD สำหรับ Java?

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