Niedawno pisałem mały fragment kodu, który w przyjazny dla człowieka sposób wskazywałby, ile lat ma wydarzenie. Może to na przykład oznaczać, że wydarzenie miało miejsce „Trzy tygodnie temu”, „Miesiąc temu” lub „Wczoraj”.
Wymagania były stosunkowo jasne i był to idealny przypadek dla rozwoju opartego na testach. Testy pisałem jeden po drugim, wdrażając kod, aby przejść każdy test i wszystko wydawało się działać idealnie. Do momentu pojawienia się błędu w produkcji.
Oto odpowiedni fragment kodu:
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.
Testy sprawdzały przypadek zdarzenia, które miało miejsce dzisiaj, wczoraj, cztery dni temu, dwa tygodnie temu, tydzień temu itp., A kod został odpowiednio zbudowany.
Tęskniłem za tym, że wydarzenie może zdarzyć się dzień przed wczoraj, a być dzień temu: na przykład wydarzenie, które wydarzyło się dwadzieścia sześć godzin temu, byłoby dzień temu, a nie dokładnie wczoraj, jeśli teraz jest 1 rano. Dokładniej, to jeden punkt coś, ale ponieważ delta
jest liczbą całkowitą, będzie tylko jedna. W takim przypadku aplikacja wyświetla „One days ago”, co jest oczywiście nieoczekiwane i nieobsługiwane w kodzie. Można to naprawić, dodając:
if delta == 1:
return "A day ago"
zaraz po obliczeniu delta
.
Chociaż jedyną negatywną konsekwencją tego błędu jest to, że zmarnowałem pół godziny zastanawiając się, jak może się zdarzyć ta sprawa (i wierząc, że ma to związek ze strefami czasowymi, pomimo jednolitego użycia UTC w kodzie), niepokoi mnie jej obecność. Wskazuje, że:
- Bardzo łatwo jest popełnić błąd logiczny nawet w tak prostym kodzie źródłowym.
- Rozwój oparty na testach nie pomógł.
Niepokojące jest również to, że nie widzę, jak można uniknąć takich błędów. Pomijając więcej myślenia przed napisaniem kodu, jedyne, co mogę wymyślić, to dodać wiele stwierdzeń dla przypadków, które moim zdaniem nigdy by się nie wydarzyły (tak jak myślałem, że dzień temu jest koniecznie wczoraj), a następnie przeglądać co sekundę w ciągu ostatnich dziesięciu lat sprawdzanie, czy nie dochodzi do naruszenia twierdzeń, które wydają się zbyt skomplikowane.
Jak mogę uniknąć tworzenia tego błędu?