ทารกเป็นขั้นตอนลูกน้อยของคุณใน TDD อย่างไร


37

วันนี้เรากำลังฝึกอบรม TDD และพบประเด็นต่อไปนี้ของความเข้าใจผิด

งานสำหรับอินพุต "1,2" ผลรวมของตัวเลขซึ่งคือ 3 สิ่งที่ฉันเขียน (ใน C #) คือ:

numbers = input.Split(',');
return int.Parse(numbers[0]) + int.Parse(numbers[1]); //task said we have two numbers and input is correct

แต่คนอื่นชอบที่จะทำอย่างอื่น ก่อนอื่นสำหรับการป้อน "1,2" พวกเขาได้เพิ่มรหัสต่อไปนี้:

if (input == "1,2")
   return 3;

จากนั้นพวกเขาแนะนำการทดสอบอีกหนึ่งครั้งสำหรับอินพุต "4,5" และการใช้งานที่เปลี่ยนแปลง:

if (input == "1,2")
   return 3;
else if (input == "4,5")
   return 9;

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

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

ปรับปรุง ฉันทำผิดพลาดโดยไม่ชี้แจงว่าไม่ใช่การทดสอบครั้งแรก มีการทดสอบอยู่แล้วดังนั้น "การคืน 3" จริงๆแล้วไม่ใช่รหัสที่ง่ายที่สุดในการตอบสนองความต้องการ


25
ดังนั้นเล็ก ๆ ที่เพื่อนร่วมงานของฉันพรั่งพรู "Ooahhh dazso cuuuuuute"
อาเดล

6
@Adel: เกือบสำลักอาหารเช้าของฉันแป้นพิมพ์ตอนนี้เต็มหรือ spittle และเศษเล็กเศษน้อย
Binary Worrier

2
@Adel สำหรับผู้พูดที่ไม่ใช่เจ้าของภาษามันค่อนข้างยากสำหรับฉันที่จะเข้าใจอารมณ์ขันนี้ แต่ฉันเดาว่าเพื่อนร่วมงานของคุณชอบคำถาม :)
SiberianGuy

8
@Idsa: มันเป็นคำตอบของเพื่อนร่วมงานเมื่อแสดงขั้นตอนแรกของเด็ก ๆ "Ooahhh dazso cuuuuuute" = "โอ้นั่นช่างน่ารักเหลือเกิน" (พูดด้วยเสียงร้องเพลง - ไม่ใช่เสียงที่น่ารักมาก ๆ ) พร้อมการตอบกลับของพวกเขา เมื่อเห็นการทดสอบหน่วยที่เขียนโดย Adel ดูที่ขั้นตอนทารกของการทดสอบหน่วยที่พวกเขาพูดว่า "โอ้ช่างน่ารักเหลือเกิน" ปฏิกิริยาต่อขั้นตอนทารก - ของจริง = ปฏิกิริยาต่อการทดสอบหน่วย "ก้าวทารก"
Binary Worrier

3
@ Binaryworrier หวังว่าฉันจะให้คะแนนที่แท้จริงสำหรับการสละเวลาในการอธิบายผู้ปกครอง
Andrew T Finnell

คำตอบ:


31

เขียนรหัสที่ง่ายที่สุดที่ทำให้การทดสอบผ่าน

คุณไม่ทำอย่างนั้นเท่าที่ฉันเห็น

ขั้นตอนที่ 1 ทารก

ทดสอบ: สำหรับอินพุต "1,2" ส่งคืนผลรวมของตัวเลขซึ่งคือ 3

ทำให้การทดสอบล้มเหลว:

throw NotImplementedException();

ทำการทดสอบผ่าน:

return 3;

ขั้นตอนที่ 2

ทดสอบ: สำหรับอินพุต "1,2" ส่งคืนผลรวมของตัวเลขซึ่งคือ 3

ทดสอบ: สำหรับอินพุต "4,5" ส่งคืนผลรวมของตัวเลขซึ่งคือ 9

การทดสอบครั้งที่สองล้มเหลว

numbers = input.Split(',');
return int.Parse(numbers[0]) + int.Parse(numbers[1]);

(วิธีง่ายกว่ารายการถ้า ... ส่งคืน)

คุณสามารถโต้แย้งการใช้งานที่เห็นได้ชัดในกรณีนี้ แต่ถ้าคุณกำลังพูดถึงการทำอย่างเคร่งครัดในขั้นตอนเด็กนี่คือขั้นตอนที่ถูกต้อง IMO

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

return input.Length; # Still satisfies the first test

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


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

@Idsa - ใช่แน่นอนและยิ่งคุณทดสอบมากเท่าไหร่ input.Lengthคือไม่ว่าไกลเรียกโดยเฉพาะอย่างยิ่งถ้าใส่กับวิธีการที่เกิดขึ้นจะวัดจากที่ไหนสักแห่งไฟล์และคุณ inadvisedly Size()เรียกว่าวิธีการของคุณ
pdr

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

1
ฉันมีคำถามเกี่ยวกับ "ทดสอบ" ตัวเอง คุณจะเขียนการทดสอบใหม่สำหรับอินพุต "4,5" หรือปรับเปลี่ยนการทดสอบเดิมได้หรือไม่
mxmissile

1
@mxmileile: ฉันจะเขียนแบบทดสอบใหม่ ไม่ต้องใช้เวลามากและคุณต้องจบการทดสอบสองเท่าเพื่อปกป้องคุณเมื่อคุณทำการปรับโครงสร้างใหม่ในภายหลัง
pdr

50

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

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


+1 และขอขอบคุณที่ทำให้ฉันค้นหาและเรียนรู้คำศัพท์ใหม่ (asinine)
Marjan Venema

12
+1 สำหรับการเรียกมันว่าโง่งี่เง่า TDD นั้นดีมาก แต่ก็เหมือนกับเทคนิคการเขียนโปรแกรม hyped สมัยใหม่ที่คุณควรระวังไม่ให้หลงทาง
stijn

2
"โดยเฉพาะอย่างยิ่งหากปัญหาดั้งเดิมที่คุณแก้ไขมีขนาดเล็กมากโดยเป็นของตัวเอง" - หากอินพุตเป็นสอง ints ที่จะรวมเข้าด้วยกันฉันจะเห็นด้วยกับสิ่งนี้ แต่ฉันไม่มั่นใจเมื่อมัน "แยกสตริง, แยกสอง ints จากผลและเพิ่มพวกเขา" วิธีการส่วนใหญ่ในโลกแห่งความเป็นจริงไม่ซับซ้อนกว่านั้น ในความเป็นจริงควรจะมีการทดสอบขึ้นมาเพื่อกรณีฝาครอบขอบเหมือนการหาเครื่องหมายจุลภาคสองค่าที่ไม่ใช่จำนวนเต็ม ฯลฯ
สาธารณรัฐประชาธิปไตยประชาชนลาว

4
@pdr: ฉันเห็นด้วยกับคุณว่าควรมีการทดสอบเพิ่มเติมเพื่อจัดการกับกรณีขอบ เมื่อคุณเขียนและสังเกตเห็นว่าการใช้งานของคุณจำเป็นต้องเปลี่ยนเพื่อจัดการกับพวกเขาโดยทั้งหมดทำเช่นนั้น ฉันเดาว่าฉันมีปัญหากับการทำตามขั้นตอน zygote สู่เส้นทางแห่งความสุขครั้งแรก "การใช้งานที่ชัดเจน" แทนที่จะเขียนลงไปและไปจากที่นั่น ฉันไม่เห็นคุณค่าในการเขียนคำสั่ง if ว่าเส้นใยทุกตัวในร่างกายของฉันรู้ว่ากำลังจะหายไปในวินาทีถัดไป
Christophe Vanfleteren

1
@ChristopheVanfleteren: เมื่อเบ็คอธิบายการใช้งานที่ชัดเจนเขาใช้ผลรวมของสอง ints เป็นตัวอย่างและยังคงโยนคำเตือนอย่างมากเกี่ยวกับวิธีการที่คุณจะตายจากความอับอายหากคู่ / ผู้วิจารณ์ของคุณสามารถคิดรหัสง่ายขึ้นที่ทำให้ ผ่านการทดสอบ นั่นคือความเชื่อมั่นแน่นอนถ้าคุณเขียนการทดสอบหนึ่งสำหรับสถานการณ์นี้ นอกจากนี้ฉันสามารถคิดถึงวิธี "ชัดเจน" อย่างน้อยสามวิธีในการแก้ปัญหานี้: แยกและเพิ่มแทนที่เครื่องหมายจุลภาคด้วย + และประเมินหรือใช้ regex จุดสำคัญของ TDD คือการนำคุณไปสู่ทางเลือกที่ถูกต้อง
pdr

19

Kent Beck ครอบคลุมเนื้อหานี้ในหนังสือของเขาที่ชื่อว่า Test Driven Development: ตามตัวอย่าง

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

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

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

จากหนังสือ:

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


18

ฉันเห็นสิ่งนี้ตามตัวอักษรของกฎหมาย แต่ไม่ใช่วิญญาณของมัน

ขั้นตอนลูกน้อยของคุณควรจะ:

ง่ายที่สุดเท่าที่จะเป็นไปได้ แต่ไม่ง่ายกว่านี้

นอกจากนี้คำกริยาในวิธีการคือ sum

if (input == "1,2")
   return 3;

ไม่ใช่ผลรวม แต่เป็นการทดสอบอินพุตเฉพาะ


4

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

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


1

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

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

คลาสนี้ที่ฉันมีอยู่ในหัวของฉันจะไปทำงานจริงหรือไม่?

หรือ

ฉันจะทำสิ่งนี้ได้อย่างไร

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

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

ฉันหวังว่าคุณจะสนุกกับการฝึกของคุณติดกับ TDD IMHO ถ้ามีคนติดเชื้อมากขึ้นโลกก็น่าจะดีขึ้น :)


1

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

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

ดังนั้นบางทีเพื่อนร่วมงานของคุณอาจเป็นคนขี้อายนิดหน่อย :)


1

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


1
มันไม่เกี่ยวข้องถ้าคุณไม่สนใจการเสียเวลาของคุณโดยสิ้นเชิง
SiberianGuy

1

ฉันเห็นด้วยกับผู้คนที่บอกว่าไม่มีวิธีที่ง่ายที่สุด

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


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