เมื่อไม่นานมานี้ฉันกำลังเขียนโค้ดขนาดเล็กซึ่งจะบ่งบอกถึงวิธีการที่เป็นมิตรกับมนุษย์ว่าเหตุการณ์มีอายุเท่าไร ตัวอย่างเช่นอาจบ่งบอกว่าเหตุการณ์เกิดขึ้น“ สามสัปดาห์ที่ผ่านมา” หรือ“ เดือนที่แล้ว” หรือ“ เมื่อวานนี้”
ความต้องการค่อนข้างชัดเจนและนี่เป็นกรณีที่สมบูรณ์แบบสำหรับการพัฒนาแบบทดสอบที่ขับเคลื่อน ฉันเขียนการทดสอบทีละตัวการใช้รหัสเพื่อผ่านการทดสอบแต่ละครั้งและทุกอย่างดูเหมือนจะทำงานได้อย่างสมบูรณ์ จนกว่าจะพบข้อบกพร่องในการผลิต
นี่คือโค้ดที่เกี่ยวข้อง:
now = datetime.datetime.utcnow()
today = now.date()
if event_date.date() == today:
return "Today"
yesterday = today - datetime.timedelta(1)
if event_date.date() == yesterday:
return "Yesterday"
delta = (now - event_date).days
if delta < 7:
return _number_to_text(delta) + " days ago"
if delta < 30:
weeks = math.floor(delta / 7)
if weeks == 1:
return "A week ago"
return _number_to_text(weeks) + " weeks ago"
if delta < 365:
... # Handle months and years in similar manner.
การทดสอบกำลังตรวจสอบกรณีของเหตุการณ์ที่เกิดขึ้นวันนี้เมื่อวานนี้สี่วันที่ผ่านมาสองสัปดาห์ที่ผ่านมาสัปดาห์ที่แล้ว ฯลฯ และรหัสถูกสร้างขึ้นตามลำดับ
สิ่งที่ฉันพลาดคือเหตุการณ์ที่เกิดขึ้นหนึ่งวันก่อนหน้านี้เมื่อวานนี้ในขณะที่เป็นหนึ่งวันที่ผ่านมา: ตัวอย่างเช่นเหตุการณ์ที่เกิดขึ้นเมื่อยี่สิบหกชั่วโมงที่ผ่านมาจะเป็นหนึ่งวันที่ผ่านมา บางอย่าง แต่เนื่องจากdelta
เป็นจำนวนเต็มมันจะเป็นเพียงหนึ่ง ในกรณีนี้แอปพลิเคชันจะแสดง“ หนึ่งวันที่ผ่านมา” ซึ่งเห็นได้ชัดว่าไม่คาดคิดและไม่สามารถจัดการได้ในโค้ด สามารถแก้ไขได้โดยการเพิ่ม:
if delta == 1:
return "A day ago"
delta
หลังจากการคำนวณ
ในขณะที่ผลลัพธ์เชิงลบข้อผิดพลาดเพียงอย่างเดียวของฉันคือฉันเสียเวลาครึ่งชั่วโมงสงสัยว่ากรณีนี้จะเกิดขึ้น (และเชื่อว่ามันจะทำอย่างไรกับโซนเวลาแม้จะใช้เครื่องแบบ UTC ในรหัส) การปรากฏตัวของมันทำให้ฉันหนักใจ มันบ่งบอกว่า:
- มันง่ายมากที่จะยอมรับความผิดพลาดเชิงตรรกะแม้ในซอร์สโค้ดที่ง่ายเช่นนั้น
- การพัฒนาที่ขับเคลื่อนด้วยการทดสอบไม่ได้ช่วยอะไร
ที่น่าเป็นห่วงก็คือฉันไม่สามารถดูได้ว่าข้อบกพร่องดังกล่าวสามารถหลีกเลี่ยงได้อย่างไร นอกเหนือจากการคิดก่อนเขียนรหัสวิธีเดียวที่ฉันสามารถคิดได้คือเพิ่มการยืนยันจำนวนมากสำหรับกรณีที่ฉันเชื่อว่าจะไม่เกิดขึ้น (เช่นฉันเชื่อว่าเมื่อวานนี้เป็นวันวานจำเป็นแล้ว) จากนั้นวนซ้ำทุกวินาที สิบปีที่ผ่านมาตรวจสอบการละเมิดการยืนยันใด ๆ ซึ่งดูเหมือนซับซ้อนเกินไป
ฉันจะหลีกเลี่ยงการสร้างข้อผิดพลาดนี้ตั้งแต่แรกได้อย่างไร