Nie obsługuj wyjątków podczas testowania

Unikaj używania bloków Try Catch w kodzie testowym z przykładami Pythona

Wyjątki

Wyjątkami są błędy występujące w czasie wykonywania.

Myślę, że istnieją 2 główne powody, aby obsługiwać wyjątki w programie.

  1. Aby uniknąć awarii programu.
  2. Aby podjąć pewne działania w wyniku błędu.

Wczesne uruchamianie to uzasadniona strategia niezawodności omówiona w książce The Pragmatic Programmer autorstwa Davida Thomasa i Andrew Hunta.

Ale nie wszystkie błędy muszą być śmiertelne.

Testowanie

Testując oprogramowanie, Twoim celem jest przetestowanie (uruchomienie) oprogramowania i sprawdzenie, czy działa zgodnie z zestawem oczekiwań (wymagań). Istnieje wiele technik i metod oraz różnych rodzajów testów. Ale to jest sedno sprawy.

Kiedy przeprowadzasz automatyczne testy jednostkowe lub funkcjonalne, technicznie rzecz biorąc, piszesz program w celu sprawdzenia innego programu. Z tego powodu możesz rozsądnie założyć, że aby testy były wiarygodne, powinieneś upewnić się, że program testowy nie ulegnie awarii podczas testowania, i dlatego dodać dużą ilość kodu obsługującego błędy.

Jednak naprawdę ważne jest, aby zrozumieć, że oprogramowanie uruchamiające testy ma już wbudowaną obsługę wyjątków! Oznacza to, że jeśli metoda testowa zgłosi nieprzechwycony wyjątek, cały program nie ulegnie awarii. Metodę tę po prostu liczy się jako nieudany test. Wszystkie pozostałe metody nadal działają i zgłaszają swoje wyniki.

Ogólnie rzecz biorąc, wyjątki należy obsługiwać podczas testowania tylko wtedy, gdy testujesz scenariusze błędów i chcesz dokładnie sprawdzić, czy program zareaguje w oczekiwany sposób.

Przykłady

W przykładach zastosowano test jednostkowy Pythona, ale jest to ogólna rada.

Tworzę moduł, który definiuje funkcję, która po prostu zwraca wynik dzielenia 2 argumentów wejściowych.

To naprawdę prosty przykład, ale musisz sobie wyobrazić, że może to być bardzo złożony program, dla którego albo przeprowadzasz testy jednostkowe, albo piszesz automatyczne testy funkcjonalne.

Zły przykład

FAIL: test_divide_unexpected_fail (__main__.TestDivide)
We know this will throw a Runtime Error. But imagine we didn't.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_divide_bad.py", line 24, in test_divide_unexpected_fail
    self.assertEqual(divide.divide(10, 0), 3)
ZeroDivisionError: division by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "test_divide_bad.py", line 26, in test_divide_unexpected_fail
    assert False
AssertionError
----------------------------------------------------------------------
Ran 4 tests in 0.001s
FAILED (failures=1)

Kod testowy jest szczegółowy i nie przynosi żadnych dodatkowych wartości, jak zobaczysz w następnym przykładzie. W tym przypadku możemy zobaczyć ślad błędu do celów debugowania, ale widziałem, gdzie obsługa błędów faktycznie przesłania rzeczywisty błąd w środowisku naturalnym.

Dobry przykład

ERROR: test_divide_unexpected_fail (__main__.TestDivide)
We know this will throw a Runtime Error. But imagine we didn't.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_divide_good.py", line 17, in test_divide_unexpected_fail
    self.assertEqual(divide.divide(10, 0), 3)
  File "a/b/c/divide.py", line 2, in divide
    return a/b
ZeroDivisionError: division by zero
----------------------------------------------------------------------
Ran 4 tests in 0.001s
FAILED (errors=1)

Jeśli test powoduje wyjątek, po prostu kończy się niepowodzeniem z powodu błędu. Jest to znacznie czystsze, a wynik śledzenia błędów powiązany z metodą bardzo wyraźnie pokazuje, który problem wystąpił.

Wreszcie

Być może najlepszym sposobem, aby zrozumieć, że testy mają wbudowaną obsługę błędów, jest zrozumienie, że nieudana asercja generuje wyjątek! Błąd asercji.

Zatem następujący test zostanie zaliczony:

Mamy nadzieję, że nauczyłeś się czegoś, czytając to, i być może będziesz w stanie gdzieś uporządkować test lub dwa.