pyTest Tutorial #5: Konstrukcja testu

pyTest Tutorial #5: Konstrukcja testu

Poziom trudności
2.1/5

Mając już wiedzę dotyczącą uruchamiania testów, a także związaną z kluczowym elementem testu, czyli asercji, możemy przyjrzeć się konstrukcji samego testu. Częściowo dzisiejsza odsłona będzie podsumowaniem dotychczas zdobytej wiedzy. Poznamy w niej dodatkowo kilka części składowych, które powinien zawierać każdy test. Ponadto przedstawimy elementy, które są opcjonalne. 

Ogólna konstrukcja testów

Większość testów pisanych przy użyciu pyTest’a będzie się opierało na takim samym schemacie:

Dla większości testerów takie zestawienie nie powinno być niczym nowym, gdyż są to podstawowe elementy każdego, nawet nieautomatycznego testu. Jedyną różnicą jest rozbicie kroków testu na akcje oraz asercje.

Definicja funckji testującej

Jak już wspominałem w jednej z poprzednich części, definicja funkcji testujące musi spełniać tylko dwa warunki. Pierwszym z nich jest spełnianie wymogów dotyczących nazwy standardowej funkcji, czyli na przykład nie może się rozpoczynać od cyfry. Drugim warunkiem jest dopasowanie do jednego z poniższych wzorców. Dzięki temu informujemy pyTesta, które funkcje są funkcjami pomocniczymi, a które są testami:

Warunki wstępne

Warunki wstępne przygotowują środowisko do przeprowadzenia testu, oczywiście, jeśli takie przygotowanie jest wymagane. Nie inaczej jest w przypadku funkcji testujących. Na chwilę obecną, ustawianie warunków początkowych można umieścić w ciele testu, jednak już niedługo poznamy o wiele ciekawsze sposoby na ich wprwoadzanie.

Akcje testu

W przypadku bardzo prostych testów może nie być potrzebnych dodatkowych akcji w teście, jednak w znacznej większości jest to element wymagany. Są to nic innego jak kroki testu. W zależności od tego co udostępnia nam framework łączący nas z programem testowanym, mogą one zawierać interakcję z systemem.

Asercja(e)

I w końcu doszliśmy do najważniejszego elementu testu, bez którego żaden test automatyczny nie miałby sensu, gdyż nie byłoby elementu decydującego o jego pozytywnym lub negatywnym zakończeniu – oczywiście pomijając wszystkie wyjątki :). W każdym teście z reguły znajduje się jedna lub więcej asercji. Zostały one szczegółowo opisane w poprzedniej części.

Czynności zamykające test

Jest to ostatni element testu, którego istnienie jest w wielu przypadkach niezbędne. Służy on do wykonania czynności, które posprzątają po teście. Można oczywiście umieścić je w ciele testu, bezpośrednio po asercjach, jednak takie rozwiązanie posiada wiele wad — na przykład w przypadku niepowodzenia testu, czynności te się nie wykonają. W kolejnych częściach poznamy, jakie są polecane praktyki w tym zakresie. 

Piszemy testy w pyTest

Napiszmy teraz kilka testów, które będą sprawdzały kod klasy BasicCalculator z pliku base.py, stworzonej w części pierwszej:

W części pierwszej utworzyliśmy również dwa testy w pliku test_calc.py. Plik zaczyna się od importu klasy, którą będziemy testować. Klasa ta znajduje się w pliku base, który jest w tej samej lokalizacji co plik z testami, także możemy z powodzeniem wykonać import jak w przykładzie poniżej. W poniższych testach nie będziemy mieli potrzeby wykorzystywania czynności otwierających oraz zamykających test. Z racji, że będziemy chcieli dokładniej przetestować funkcjonalności dodawania, zmieńmy nazwy stworzonych w pierwszej części testów, zgodnie z poniższym. 

  1. from base import BasicCalculator
  2. def test_addition_10_5():
  3. object = BasicCalculator()
  4. object.provide_number(10)
  5. object.provide_operand('+')
  6. object.provide_number(5)
  7. assert object.show_result()[0] == 15
  8. def test_subtraction_13_21():
  9. object = BasicCalculator()
  10. object.provide_number(13)
  11. object.provide_operand('-')
  12. object.provide_number(21)
  13. assert object.show_result()[0] == -8

Teraz możemy dopisać kilka testów zarówno do funkcji dodawania, jak i odejmowania:

  1. def test_addition_2_m4():
  2. object = BasicCalculator()
  3. object.provide_number(2)
  4. object.provide_operand('+')
  5. object.provide_number(-4)
  6. assert object.show_result()[0] == -2
  7. def test_addition_m10_12():
  8. object = BasicCalculator()
  9. object.provide_number(-10)
  10. object.provide_operand('+')
  11. object.provide_number(12)
  12. assert object.show_result()[0] == 2
  13. def test_subtraction_m3_m7():
  14. object = BasicCalculator()
  15. object.provide_number(-3)
  16. object.provide_operand('-')
  17. object.provide_number(-7)
  18. assert object.show_result()[0] == 4

Oraz do niepokrytych jeszcze warunków funkcjonalności mnożenia, oraz dzielenia:

  1. def test_multiplication_2_2():
  2. object = BasicCalculator()
  3. object.provide_number(2)
  4. object.provide_operand('*')
  5. object.provide_number(2)
  6. assert object.show_result()[0] == 4
  7. def test_multiplication_m7_3():
  8. object = BasicCalculator()
  9. object.provide_number(-7)
  10. object.provide_operand('*')
  11. object.provide_number(3)
  12. assert object.show_result()[0] == -21
  13. def test_division_10_2():
  14. object = BasicCalculator()
  15. object.provide_number(10)
  16. object.provide_operand('/')
  17. object.provide_number(2)
  18. assert object.show_result()[0] == 5
  19. def test_division_12_0():
  20. object = BasicCalculator()
  21. object.provide_number(12)
  22. object.provide_operand('/')
  23. object.provide_number(0)
  24. assert object.show_result()[0] == 5

Mając już całkiem spory zestaw testów, możemy przejść do ich uruchomienia. Wykonamy to zgodnie ze sposobami uruchamiania testów, jakie poznaliśmy w poprzednich częściach:

(env) qabrio@test:~/basic_calculator$ pytest test_calc.py -v --tb=line
========================================= 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: .cache
rootdir: /home/qabrio/basic_calculator, inifile:
collected 9 items
test_calc.py::test_addition_10_5 PASSED [ 11%]
test_calc.py::test_subtraction_13_21 PASSED [ 22%]
test_calc.py::test_addition_2_m4 PASSED [ 33%]
test_calc.py::test_addition_m10_12 PASSED [ 44%]
test_calc.py::test_subtraction_m3_m7 PASSED [ 55%]
test_calc.py::test_multiplication_2_2 PASSED [ 66%]
test_calc.py::test_multiplication_m7_3 PASSED [ 77%]
test_calc.py::test_division_10_2 PASSED [ 88%]
test_calc.py::test_division_12_0 FAILED [100%]
================================================ FAILURES =============================================
/home/qabrio/basic_calculator/base.py:26: ZeroDivisionError: integer division or modulo by zero
================================= 1 failed, 8 passed in 0.02 seconds ==================================
(env) qabrio@test:~/basic_calculator$

Podsumowując, udało nam się przebrnąć przez konstrukcję pisania, a także napisaliśmy kilka dodatkowych testów do klasy BasicCalculator. Oczywiście nie są to wszystkie możliwe testy, dlatego też, w kolejnej części poznamy możliwe sposoby parametryzowania testów. Jest to bardzo przydatne narzędzie, dlatego zapraszam do lektury!

pyTest Tutorial #5: Konstrukcja testu image

Dodaj komentarz