pyTest: DDT w praktyce #3

pyTest: DDT w praktyce #3

Poziom trudności
4.5/5

W związku z pytaniami dotyczącymi uruchamiania testów z wykorzystaniem DDT oraz pyTesta, a także uciążliwej edycji kodu w celu zmiany pliku parametryzacyjnego do testów, postanowiłem rozwinąć ten temat w tym artykule. Dodatkowo odpowiem na pytanie, w którym miejscu najlepiej umieścić konfigurację odpowiedniego wariantu w urządzeniu do testów. W przytoczonych poniżej przykładach wykorzystam testy oraz parametry stworzone w poprzednim artykule z serii Data Driven Testing, czyli Testowaniu Sterowanym Danymi.

Przygotowanie importu parametrów

Aby móc wykorzystać automatyczną zmianę parametrów, niezbędne jest wyedytowanie stworzonego wcześniej kodu importu parametrów. Można to wykonać z wykorzystaniem dodatkowej biblioteki importlib. Zamiast wcześniej używanego importu parametrów:

  1. from input_c import input_operation, input_power_function

wykorzystamy:

  1. import importlib
  2. params = importlib.import_module("input_a")

Następnie musimy dodać params w parametryzacji testów:

  1. @pytest.mark.parametrize('numA, operation, numB, result', params.input_operation)
  2. (...)
  3. @pytest.mark.parametrize('number, result', params.input_power_function)
  4. (...)

W tym momencie, podając jako argument dla import_module, dowolny plik z parametrami, będzie on wykorzystany w testach.

Dodanie argumentu dla komendy uruchamiającej testy

Idąc dalej, jesteśmy w stanie stworzyć argument dla pyTesta, który pozwoli nam uruchomić testy z dowolnym plikiem parametryzacyjnym korzystając jedynie z konsoli. Posłużymy się tutaj plikiem conftest.py. Jest to plik, który konfiguruje testy dla konkretnego katalogu. Pliku tego nie ma domyślnie w naszym katalogu z testami, dlatego zaczniemy od jego stworzenia:

qabrio@test:~$ touch conftest.py

Następnie z wykorzystaniem wbudowanego inicjalizującego pytests hook (wbudowane pytestowe funkcje umożliwiające ingerencję w uruchamianie, zbieranie czy raportowanie testów) – pytest_addoption, tworzymy dodatkowy atrybut variantName, wykorzystywany podczas uruchamiania testów w pyTest:

  1. import pytest
  2. def pytest_addoption(parser):
  3. parser.addoption("--variantName", action="store", default="input_a")

W powyższym przykładzie, jako pierwszy argument w wykorzystanej funkcji pytest_addoption, podajemy jego nazwę, następnie akcję do wykonania oraz wartość domyślną, która będzie wykonana w sytuacji, gdy nie wykorzystamy atrybutu podczas uruchomienia testów. Z racji, że wariant A uznajemy za bazowy, umieściłem plik z jego parametrami jako domyślny.

Przekazanie atrybutu do testów

Mając już możliwość podania dodatkowego argumentu, dobrze byłoby go wykorzystać :). Z pomocą przyjdzie nam kolejny pytestowy hook, czyli pytest_configure. Dzięki niemu dodamy do przestrzeni nazw pytest, nazwę testowanego wariantu:

  1. def pytest_configure(config):
  2. pytest.variantName = config.getoption('--variantName')

Mając już dostęp do podanej nazwy pliku parametryzacyjego, na chwilę wrócimy do pliku zawierającego testy, zmieniając argument w import_module na pytest.variantName. Aktualnie import wygląda tak:

  1. params = importlib.import_module(pytest.variantName)

Konfiguracja urządzenia

Oczywiście każde urządzenia posiada swoją konfigurację, dlatego też posłużę się tutaj lekkim uproszczeniem. Załóżmy, że konfiguracja naszego środowiska znajduje się w pliku device_config.py, który wygląda jak poniżej:

  1. def set_device(deviceName):
  2. if deviceName == "input_b":
  3. _set_variant_b()
  4. elif deviceName == "input_c":
  5. _set_variant_c()
  6. else:
  7. _set_variant_a()
  8. def _set_variant_a():
  9. print("<<>>")
  10. def _set_variant_b():
  11. print("<<>>")
  12. def _set_variant_c():
  13. print("<<>>")

Teraz wystarczy zaimportować powyższy plik w pliku conftest.py, a następnie wykorzystać w nim funkcję wykonującą wstępną konfigurację:

  1. import pytest
  2. from device_config import set_device
  3. def pytest_addoption(parser):
  4. parser.addoption("--variantName", action="store", default="input_b")
  5. def pytest_configure(config):
  6. pytest.variantName = config.getoption('--variantName')
  7. set_device(pytest.variantName)

Prostsze uruchamianie testów?

Mając już odpowiednio przygotowane środowisko, możemy w prosty sposób wybierać testy dla wariantu C, bezpośrednio z linii komend:

qabrio@test:~/ddt$ pytest -v test_calculator.py --variantName input_c
<<>>
================================================== test session starts ==================================================
(...)
collected 6 items
test_calculator.py::test_operation[1-*-255-255] PASSED [ 16%]
test_calculator.py::test_operation[255-+-0-255] PASSED [ 33%]
test_calculator.py::test_operation[100---255--155] PASSED [ 50%]
test_calculator.py::test_operation[170---32-5.31] PASSED [ 66%]
test_calculator.py::test_power_function[0-0] PASSED [ 83%]
test_calculator.py::test_power_function[255-65025] PASSED [100%]
=============================================== 6 passed in 0.01 seconds ================================================

Analogicznie dla wariantu B:

qabrio@test:~/ddt$ pytest -v test_calculator.py --variantName input_b
<<>>
================================================== test session starts ==================================================
(...)
collected 4 items
test_calculator.py::test_operation[1-*-255-255] PASSED [ 25%]
test_calculator.py::test_operation[255-+-0-255] PASSED [ 50%]
test_calculator.py::test_operation[100---255--155] PASSED [ 75%]
test_calculator.py::test_power_function[number0-result0] SKIPPED [100%]
========================================== 3 passed, 1 skipped in 0.01 seconds ==========================================

Natomiast uruchamiając testy bez wykorzystania opcji –variantName, zostaną wykorzystane domyślne ustawienia, czyli w naszym przypadku wariant A:

qabrio@test:~/ddt2$ pytest -v test_calculator.py
<<>>
============================================================================================================ test session starts ============================================================================================================
(...)
collected 5 items
test_calculator.py::test_operation[1-*-10-10] PASSED [ 20%]
test_calculator.py::test_operation[10-+-0-10] PASSED [ 40%]
test_calculator.py::test_operation[5---10--5] PASSED [ 60%]
test_calculator.py::test_power_function[1-1] PASSED [ 80%]
test_calculator.py::test_power_function[255-65025] PASSED [100%]
========================================================================================================= 5 passed in 0.01 seconds ==========================================================================================================

Jak widać na powyższych przykładach, uruchamianie testów uprościło się w znacznym stopniu. Dzięki temu nie jesteśmy juz skazani na jakiekolwiek edytowanie przygotowanego kodu. Dodatkowo możemy w łatwy sposób uruchomić testy z innych narzędzi testowych, czy choćby z wykorzystaniem tabeli linuksowego crona.

Dodaj komentarz