pyTest Tutorial #4: Asercje i najczęstsze błędy
Kolejna części pyTest Tutorial czyli asercje – jak je używać, a także poznamy najczęstsze błędy, które można popełnić przy ich tworzeniu.
W poprzednich częściach nauczyliśmy się jak zainstalować pyTest oraz jak uruchomić w nim testy. W tej części skupimy się na jednym z podstawowych elementów tworzenia testów. Poznamy nie tylko czym są, ale także jak używać asercje. Dodatkowo poznamy najczęstsze błedy jakie można popełnić podczas ich tworzenia.
Asercje
W poprzednich częściach kilka razy wspominałem już o asercjach, jednak dotąd nie wyjaśniłem, czym one tak naprawdę są. Więc służą one do oznaczania miejsc, w których zakładamy rezultat pozytywny. Jednocześnie jest to informacja dla pyTest’a dotycząca rezultatu testu. Aby stworzyć asercję, należy poprzedzić wyrażenie logiczne słowem assert. Istnieje wiele możliwości na tworzenie asercji. Kilka z nich przedstawiam poniżej (zmienna result przechowuje wynik naszego działania):
assert result < 5
assert 5 == result
assert result is True
assert result is False or result == 34
assert result
assert result is not 22
assert not result
Jak widzimy asercje mogą opierać się na wielu różnorakich wyrażeniach logicznych. Oczywiście test, w którym nie będzie żadnej asercji, lub będzie posiadał asercje ze stałym wynikiem będzie również działał — lecz będzie pozbawiony sensu. Poniżej dwa antyprzykłady:
def test_nuumber():
assert 5
def test_something():
action1()
action2()
Co ciekawe, takie testy mogą zostać uruchomione, lecz ich rezultat będzie zawsze ten sam — w podanych przykładach POZYTYWNY!!!:
qabrio@test:~/tmp/wrong_tests$ pytest test_file.py -v
========================================= test session starts =========================================
platform linux -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 -- /home/qabrio/basic_calculator/env/bin/python3.6
cachedir: .pytest_cache
rootdir: /home/qabrio/tmp/logging_test
collected 2 items
test_file.py::test_nuumber PASSED [ 50%]
test_file.py::test_something PASSED [100%]
======================================= 2 passed in 0.01 seconds ======================================
qabrio@test:~/tmp/$
Najczęstsze błędy
Warto w tym miejscu wspomnieć o najczęstszych błędach przy tworzeniu asercji, które mogą nie wpływać na przebieg testu, ale mogą dawać wynik fałszywie pozytywny lub fałszywie negatywny:
1. Porównanie różnych typów — jest to częsty błąd, który czasami jest trudny do znalezienia. Poniżej mamy przykład porównania rezultatu działania w formie int z liczbą 5. Pomimo że na pierwszy rzut oka wszystko wygląda dobrze, to z lewej strony będziemy mieć typ int, natomiast z drugiej łańcuch znaków str. Rezultat oczywiście FAILED.
def addition_result(a, b):
return a + b
assert addition_result(2, 3) == '5' #assert 5 == '5'
2. Asercja z jednym argumentem — pyTest umożliwia tworzenie jednoargumentowych asercji. Podchodzić do tego trzeba bardzo ostrożnie, ponieważ bardzo łatwo w nich o pomyłkę. Wbrew temu, co można by było się domyślić, wynik asercji jednoargumentowej nie jest równoznaczny z is True, lecz z is not False. W praktyce oznacza to, że asercja będzie negatywna tylko i wyłącznie dla wartości takich jak 0, False, „„ i tym podobnych. Przykład pokazany poniżej opiera się na funkcji, która przyjmuje trzy argumenty, jako składniki a, b oraz oczekiwany rezultat. W przypadku poprawnego wyniku zwraca True, w przypadku negatywnego zwraca wynik. Rezultat PASSED.
def compare_results(a, b, result):
if a + b == result:
return True
else:
return a + b
assert compare_results(3, 3, 5) #assert 6 -> PASSED
3. Zbyt złożone asercje — zaleca się, aby asercje były porównaniem dwóch wartości, czyli wyniku oczekiwanego oraz otrzymanego. Wpływa to pozytywnie na czytelność oraz zrozumienie kodu. Dodatkowo można się ustrzec przed błędami spowodowanym zrozumieniem wyrażeń logicznych. Analizując poniższy przykład, natrafimy na prosty błąd, ale jednocześnie trudny do znalezienia. Chodzi o błędną składnie wyrażenia logicznego, która powoduje, że niezależnie od wyniku funkcji result, rezultat będzie zawsze PASSED.
def result():
return 3
assert result() is 5 or True
4. Zamienne używanie == oraz is — wspomniane operatory nie są zamienne, gdyż porównują dwie różne wartości. Pierwsze, czyli == porównuje czy dany obiekt zawiera te same informacje, natomiast druga porównuje czy to jest ten sam obiekt. Rezultat PASSED.
def test_list():
list1 = [1, 2, 3]
list2 = [1, 2, 3]
assert list1 == list2
Natomiast w tym wypadku będzie rezultat FAILED.
def test_list():
list1 = [1, 2, 3]
list2 = [1, 2, 3]
assert list1 is list2
Podsumowując, udało nam się poznać w tym artykule, czym są asercje oraz jak je poprawnie używać. Ważną częścią tego artykułu są najczęściej popełniane błędy przy ich tworzeniu. Z racji, że od strony składni są one poprawne, test zostanie wykonany bezbłędnie, lecz otrzymamy rezultat fałszywie pozytywny lub fałszywie negatywny. W kolejnej częście poznamy z jakich elementów składa się test oraz napiszemy kilka przykładowych testów!