Naprawdę trudno mi napisać skuteczne testy jednostkowe dla dużego projektu Django. Mam dość dobry zasięg testów, ale zdałem sobie sprawę, że testy, które piszę, są zdecydowanie testami integracji / akceptacji, a nie testami jednostkowymi, i mam krytyczne części mojej aplikacji, które nie są testowane skutecznie. Chcę to naprawić jak najszybciej.
Oto mój problem. Mój schemat jest głęboko relacyjny i mocno zorientowany na czas, co daje mojemu obiektowi modelowemu wysokie sprzężenie wewnętrzne i dużo stanu. Wiele moich metod modelowych korzysta z zapytań opartych na przedziałach czasowych i mam dużo do czynienia z auto_now_add
polami oznaczonymi czasem . Więc weź metodę, która wygląda następująco:
def summary(self, startTime=None, endTime=None):
# ... logic to assign a proper start and end time
# if none was provided, probably using datetime.now()
objects = self.related_model_set.manager_method.filter(...)
return sum(object.key_method(startTime, endTime) for object in objects)
Jak podejść do testowania czegoś takiego?
Oto gdzie jestem do tej pory. Przyszło mi do głowy, że celowi testowania jednostkowego należy nadać pewne kpiny by key_method
z jego argumentów, czy summary
poprawnie filtruje / agreguje, aby uzyskać poprawny wynik?
Wyśmiewanie datetime.now () jest dość proste, ale jak mogę wyśmiewać resztę zachowania?
- Mógłbym używać urządzeń, ale słyszałem plusy i minusy używania urządzeń do budowania moich danych (słaba konserwacja to dla mnie oszustwo, które uderza mnie w domu).
- Mógłbym również skonfigurować moje dane za pośrednictwem ORM, ale to może być ograniczenie, ponieważ wtedy muszę również tworzyć powiązane obiekty. A ORM nie pozwala
auto_now_add
ręcznie zadzierać z polami. - Wyśmiewanie ORM to kolejna opcja, ale nie tylko trudne jest wyśmiewanie głęboko zagnieżdżonych metod ORM, ale logika w kodzie ORM zostaje wyśmiewana z testu, a wyśmiewanie wydaje się naprawdę uzależniać test od wewnętrznych elementów i zależności testowana funkcja.
Najtrudniejsze do zgryzienia wydają się takie funkcje, które są oparte na kilku warstwach modeli i funkcjach niższego poziomu i są bardzo zależne od czasu, nawet jeśli funkcje te nie są zbyt skomplikowane. Moim ogólnym problemem jest to, że bez względu na to, jak wydaje mi się to kroić, moje testy wyglądają na znacznie bardziej złożone niż funkcje, które testują.
źródło
Odpowiedzi:
Zamierzam napisać odpowiedź na to, co do tej pory wymyśliłem.
Moja hipoteza jest taka, że dla funkcji z głębokim sprzężeniem i stanem rzeczywistość jest taka, że po prostu potrzeba wielu linii do kontrolowania jej zewnętrznego kontekstu.
Oto z grubsza mój przypadek testowy, w oparciu o standardową bibliotekę próbną:
datetime
i podważamauto_now_add
czasy, aby dopasować je do ustalonej osi czasu mojego projektu. Myślałem, że ORM na to nie pozwala, ale działa dobrze.from datetime import datetime
aby można było wstawićdatetime.now()
tylko tę funkcję (jeśli wyśmiewam całądatetime
klasę, ORM sprawdza dopasowanie).object.key_method()
, z prostą, ale dobrze zdefiniowaną funkcjonalnością, która zależy od argumentów. Chcę, aby zależało to od argumentów, ponieważ w przeciwnym razie mógłbym nie wiedzieć, czy logika testowanej funkcji działa. W moim przypadku po prostu zwraca liczbę sekund międzystartTime
aendTime
. I łatam to, owijając ją w lambda i łatając bezpośrednio doobject.key_method()
używanianew_callable
kwarg zpatch
.summary
z różnymi argumentami, aby sprawdzić równość z oczekiwanymi ręcznie obliczonymi wynikami uwzględniającymi dane zachowanie próbnegokey_method
Nie trzeba dodawać, że jest to znacznie dłuższe i bardziej skomplikowane niż sama funkcja. To zależy od DB i tak naprawdę nie wydaje się testem jednostkowym. Ale jest również dość oddzielony od wewnętrznych funkcji - tylko jej sygnatury i zależności. Myślę więc, że to może być test jednostkowy.
W mojej aplikacji funkcja jest bardzo istotna i podlega refaktoryzacji w celu zoptymalizowania jej wydajności. Myślę więc, że kłopoty są tego warte, pomimo złożoności. Ale wciąż jestem otwarty na lepsze pomysły, jak do tego podejść. Cała część mojej długiej podróży w kierunku bardziej opartego na testach stylu rozwoju ...
źródło