pyTest – DDT w praktyce #3

Data Driven Testing czyli DDT w praktyce, przy użyciu Python oraz pyTesta. W części trzeciej: conftest i warianty.

Pytest DDT w praktyce

Pytest DDT w praktyce


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:

from input_c import input_operation, input_power_function

wykorzystamy:

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

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

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

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. Możemy to wyjonać 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:

import pytest
 
def pytest_addoption(parser):
    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:

def pytest_configure(config):
    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:

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:

def set_device(deviceName):
    if deviceName == "input_b":
        _set_variant_b()
    elif deviceName == "input_c":
        _set_variant_c()
    else:
        _set_variant_a()
 
def _set_variant_a():
    print("<<< KONFIGURUJĘ WARIANT A!!! >>>")
 
def _set_variant_b():
    print("<<< KONFIGURUJĘ WARIANT B!!! >>>")
 
def _set_variant_c():
    print("<<< KONFIGURUJĘ WARIANT C!!! >>>")

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

import pytest
from device_config import set_device
 
def pytest_addoption(parser):
    parser.addoption("--variantName", action="store", default="input_b")
 
def pytest_configure(config):
    pytest.variantName = config.getoption('--variantName')
    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
<<< KONFIGURUJĘ WARIANT 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
<<< KONFIGURUJĘ WARIANT 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 
<<< KONFIGURUJĘ WARIANT A!!! >>>
============================================================================================================ 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.

close

Newsletter