Použití nástroje pytest pro tvorbu jednotkových testů a benchmarků

12. 5. 2020
Doba čtení: 48 minut

Sdílet

Autor: Depositphotos
Ve třetí části seriálu o testování s využitím programovacího jazyka Python se budeme zabývat základními vlastnostmi známého nástroje pytest. Ukážeme si jak samotné použití tohoto nástroje pro jednotkové testy, tak i způsob zjištění pokrytí kódu testy, popř. vytvářením benchmarků.

Obsah

1. Použití nástroje pytest pro tvorbu jednotkových testů

2. Pořadí předávání mock objektů do funkce s dekorátorem @patch

3. Nástroj pytest – nejpřímější způsob spouštění a vyhodnocování jednotkových testů

4. Doplňkové moduly pro nástroj pytest

5. Jednoduchý projekt ukazující základní možnosti nástroje pytest

6. Spuštění nástroje pytest s vyhodnocením výsledků

7. Instalace a použití nového pluginu

8. Ladění v případě, že dochází k problémům při inicializaci pluginu

9. Zjištění pokrytí zdrojového kódu jednotkovými testy

10. Podrobnější výsledky pokrytí zdrojového kódu jednotkovými testy

11. Doplnění jedné chybějící větve do jednotkových testů

12. Využití komentáře # pragma: no cover

13. Skutečné plné pokrytí kódu testy

14. Benchmarky spouštěné v rámci jednotkových testů

15. Jednoduchý benchmark

16. Dvě verze algoritmu s jejich porovnáním

17. Porovnání výsledků benchmarku

18. Repositář s demonstračními příklady

19. Předchozí články s tématem testování (nejenom) v Pythonu

20. Odkazy na Internetu

1. Použití nástroje pytest pro tvorbu jednotkových testů

V dnešní části seriálu věnovaného testování s využitím programovacího jazyka Python nejprve dokončíme popis dekorátoru @patch z knihovny unittest.mock a posléze se budeme věnovat známému nástroji se jménem pytest, který se používá mj. i pro tvorbu jednotkových testů. Tento nástroj je užitečný i díky tomu, že pro něj vzniklo velké množství přídavných modulů, mj. i modul zjišťující pokrytí zdrojového kódu jednotkovými testy (tedy určením, které příkazy jsou skutečně zavolány a které nikoli). Ve druhé části článku si ukážeme, jakým způsobem je možné v rámci testovacího frameworku pytest spouštět benchmarky a vyhodnocovat jejich výsledky s případným porovnáním výsledků (například po provedeném refaktoringu).

Poznámka: možnosti nástroje pytest jsou ve skutečnosti mnohem rozsáhlejší a budeme se jim věnovat i v navazujícím článku.

2. Pořadí předávání mock objektů do funkce s dekorátorem @patch

V závěrečné kapitole věnované mockování s využitím knihovny unittest.mock se ještě musíme zmínit o další možná poněkud matoucí vlastnosti dekorátoru @patch. Pořadí předání reference na mockované funkce je totiž přesně opačné, než je pořadí zápisu dekorátorů! Jinými slovy – dekorátor zapsaný nejblíže definici funkce (tedy nejníže) odpovídá prvnímu parametru, dekorátor o řádek výše parametru druhému atd.

Tuto vlastnost si můžeme velmi snadno odzkoušet. Test se dvěma mocky může začínat následovně:

@patch("module1.f1", name="f1", return_value=1)
@patch("module1.f2", name="f2", return_value=2)
def test3(mocked_f2, mocked_f1):
    ...
    ...
    ...

Test se třemi mocky může vypadat například takto:

@patch("module1.f1", name="f1", return_value=1)
@patch("module1.f2", name="f2", return_value=2)
@patch("module1.f3", name="f3", return_value=3)
def test4(mocked_f3, mocked_f2, mocked_f1):
    ...
    ...
    ...

Tímto způsobem deklarované mocky jsou součástí demonstračního příkladu dostupného na adrese https://github.com/tisnik/testing-in-python/tree/master/unittest_mock/mock-testC, jehož zdrojový kód je uveden níže.

Pokud poslední příklad spustíte příkazem ./test, měly by se na standardní výstup vypsat následující řádky:

*** test1 ***
compute returns: 14
 
*** test2 ***
compute returns: 130
mocked function 'f1' called: True
calls:  [call(10)]
 
 
*** test3 ***
compute returns: 9
mocked function 'f1' called: True
calls:  [call(1)]
 
mocked function 'f2' called: True
calls:  [call(2)]
 
 
*** test4 ***
compute returns: 0
mocked function 'f1' called: True
calls:  [call(100)]
 
mocked function 'f2' called: True
calls:  [call(200)]
 
mocked function 'f3' called: True
calls:  [call(300)]

Všechny skripty, které jsou součástí tohoto demonstračního příkladu:

main.py:

#!/usr/bin/env python3
 
"""Vstupní bod do testované aplikace."""
from module1 import *
 
 
if __name__ == '__main__':
    print(compute(1, 2, 3))

module1.py:

from module2 import *
 
 
def compute(x, y, z):
    return f1(x) + f2(y) + f3(z)

module2.py:

def f1(x):
    return x
 
def f2(x):
    return 2 * x
 
def f3(x):
    return 3 * x

test.py:

"""Implementace jednotkových testů."""
 
from unittest.mock import *
 
from module1 import *
 
 
def test1():
    print("*** test1 ***")
 
    value = compute(1, 2, 3)
    print("compute returns: {v}".format(v=value))
 
 
def mock_call_info(mocked_function):
    print("mocked function '{n}' called: {c}".format(n=mocked_function._mock_name,
                                                      c=mocked_function.called))
    print("calls: ", mocked_function.mock_calls)
    print()
 
 
@patch("module1.f1", name="f1", return_value=0)
def test2(mocked_f1):
    print("*** test1 ***")
 
    value = compute(10, 20, 30)
    print("compute returns: {v}".format(v=value))
 
    mock_call_info(mocked_f1)
 
 
@patch("module1.f1", name="f1", return_value=0)
@patch("module1.f2", name="f2", return_value=0)
def test3(mocked_f2, mocked_f1):
    print("*** test3 ***")
 
    value = compute(1, 2, 3)
    print("compute returns: {v}".format(v=value))
 
    mock_call_info(mocked_f1)
    mock_call_info(mocked_f2)
 
 
@patch("module1.f1", name="f1", return_value=0)
@patch("module1.f2", name="f2", return_value=0)
@patch("module1.f3", name="f3", return_value=0)
def test4(mocked_f3, mocked_f2, mocked_f1):
    print("*** test4 ***")
 
    value = compute(100, 200, 300)
    print("compute returns: {v}".format(v=value))
 
    mock_call_info(mocked_f1)
    mock_call_info(mocked_f2)
    mock_call_info(mocked_f3)
 
 
if __name__ == '__main__':
    test1()
    print()
 
    test2()
    print()
 
    test3()
    print()
 
    test4()
    print()

3. Nástroj pytest – nejpřímější způsob spouštění a vyhodnocování jednotkových testů

Již v předchozích dvou článcích jsme si řekli že pro programovací jazyk Python existuje hned několik knihoven a frameworků určených pro spouštění a vyhodnocování jednotkových testů. Jedním z nejznámějších a nejpoužívanějších frameworků tohoto typu je pytest. Ve skutečnosti se pytest nemusí používat pouze pro zápis jednotkových testů, ale lze ho využít i pro testy komponent, fuzzy testing atd. V dnešním článku si ukážeme pouze základní možnosti použití tohoto nástroje, zejména pak:

  1. samotné spouštění jednotkových testů
  2. vyhodnocení výsledků jednotkových testů
  3. zjištění pokrytí zdrojového kódu testy
  4. spouštění benchmarků
Poznámka: některé nepatrně pokročilejší vlastnosti pytestu, zejména způsob využití takzvaných fixtures, si popíšeme v navazujícím článku.

4. Doplňkové moduly pro nástroj pytest

Vzhledem k tomu, že je pytest v současnosti považován za de facto standard v oblasti jednotkových testů pro programovací jazyk Python, vzniklo pro něj několik set(!) doplňkových modulů určených pro různé použití. Základní informace o těchto modulech (odkaz na stránku s dalšími informacemi o modulu a informace o kompatibilitě s různými verzemi Pythonu 3) jsou vypsány v následující tabulce:

Jméno py36 py37 py38 Jméno py36 py37 py38 Jméno py36 py37 py38
AN-DiscordBot-3.9.4 × ANBOT-3.1.0 × AnyPyTools-1.4.2
Blue-DiscordBot-3.2.0 × × × BotEXBotBase-3.1.3 × Bussator-0.2
CrApsim-0.0.1a0 Cuckoo-2.0.7a1 × × × Findex-GUI-0.2.18 × ×
Flask-Dance-3.0.0 × × × Kr0nOs-3.4.1 × × × Mikado-2.0rc2 × × ×
PyOTRS-0.9.0 PyTestReport-0.2.1 × × × Red-DiscordBot-3.3.7 × × ×
Socks5man-0.2.0 × × × VWS-Test-Fixtures-2019.12.13.1 × × × a2h-0.1.dev0 × × ×
abglibpythonpro-0.1 adilmar-libpythonpro-package-0.2 aguiardafa-libpythonpro-0.3
aiida-pytest-0.1.0a6 × × × aiohttp-json-rpc-0.8.4.2 aiomisc-9.8.4
aiomisc-dependency-0.1.7 × × × aioswitcher-2019.8.28 × × alauda-xdist-1.30.1
allure-pytest-2.8.9 × × × allure-pytest-acn-2.8.6 allure-pytest-bdd-2.8.9 × × ×
alt-pytest-asyncio-0.5.2 appdaemon-testing-0.1.2 × appdaemontestframework-3.0.5
automock-1.2.0 backports.unittest-mock-1.5 basepage-1.7.2
bcdc-apitests-1.0.2 × × × bcdc-apitests-dev-1.0.9 × × × beachfront-0.2.5 × × ×
bugcatcher-0.1.9 ci-watson-0.5 configa-1.1.0 × × ×
cornichon-0.9.5 ctip-0.1.0 × × × cuckoo-brood-0.1 × × ×
curio-1.2 czelta-1.0 × × × dallinger-6.2.2 × × ×
datasetops-0.0.6 datastructs-0.2.2 datatest-0.9.6
decopatch-1.4.1 × × × divide-and-cover-0.1.1 django-compress-field-0.9.9 × × ×
django-friendship-1.9.1 django-swagger-tester-1.0.2 django-test-plus-1.4.0 × × ×
django2-friendship-2.0.0 docker-integration-0.0.3 docker-services-0.3.2
egghatch-0.2.3 × × × eng-libpythonpro-0.2 enhancedschemathesis-1.0.0 × × ×
falcon-toolkit-0.0.1 × × × feedbackbot-0.0.2 × × × flake8-pytest-style-1.1.1
fpr-infra-0.1.9 × fpr-packer-0.2.3a1 × × × fsforge-0.3.7
gambit-bromine-0.3.1 ghlinguist-1.0.0 github-wrapper-0.0.9
gitlab-clone-0.1.3 godkjenn-1.1.0 goodness-of-fit-1.0.1
goodplay-0.9.1 × × × hark-1.0.0.dev3 hark-builder-1.0.0.dev6
hark-imagestore-1.0.0.dev4 hcache-1.0.3 httptesting-1.2.9 × × ×
hyaml-0.1.5 hypothesis-5.9.1 hypothesis-gufunc-0.0.6
jabstract-0.1.3 jaraco.mongodb-9.4b5 × × × jaraco.postgres-5.0 × × ×
kubetest-0.6.4 labgrid-0.1.0 × × × lcp.libpythonpro-0.2
libjonatas-0.2 liblearnpro-0.2 libpythonjp-0.2
libpythonpro-eric-0.2 libpythonpro-h-0.1 libpythonpro-leandro-1.0
libpythonpro-lm-0.2 libpythonpro-pytools-0.2 libpythonproVictor-0.4
libpythonproh-0.3 libpythonprosfx-0.2 libpythonproteste-0.2
libpythonrafael-0.0.2 limix-ext-1.0.0 logz-0.12
loofah-0.2.0rc0 mal-scraper-0.3.0 × × maxpytest-1.0.3 × × ×
mock-aerohive-0.0.2 molecule-azure-0.3 × × × molecule-containers-0.1.dev0 × × ×
molecule-digitalocean-0.1 × × × molecule-ec2–0.2 molecule-gce-0.1
molecule-libvirt-0.0.2 × × × molecule-lxd-0.1.dev0 molecule-openstack-0.1
molecule-vagrant-0.2 × × × mutatest-3.0.1 × mutmut-1.9.0
nanaimo-0.2.5 × × × nbsmoke-0.4.1 nbval-0.9.5
ncephes-1.0.5 × networktest-1.0.5 omdbapi-0.5.4
onaji-0.1.2 openapi-tester-0.0.8 × × × osa_differ-0.0.2
parasolr-0.5.2 pdfo-0.3 × × × physicl-0.0.1
pidgy-2020.4.23 × × × pipefish-0.4.2 play-selenium-0.0.3
psi-collect-0.1.3 psic-1.3.1 purerpc-0.6.1 × × ×
py-ops-0.1.4 × × × py3-bp-0.0.4 pyDbOracle-0.4.2
pyfrc-2020.1.5 × × × pymox-0.7.9.dev38 pypi-foreverchaos-test-1.0.0.0 × × ×
pypom-form-0.3.1 pysnap-1.0.1 pytedjmi-0.3.0 × × ×
pytest-Inomaly-0.2.5 × × × pytest-adaptavist-4.0.5 × × × pytest-aggreport-0.1.4
pytest-aiofiles-0.2.0 × × × pytest-aiohttp-0.3.0 pytest-aioworkers-0.3.0
pytest-airflow-0.0.3 × × × pytest-alembic-0.2.1 pytest-allclose-1.0.0
pytest-allure-adaptor-1.7.10 × pytest-allure-adaptor2–1.7.12 × pytest-allure-dsl-0.0.9 ×
pytest-alphamoon-0.3.0 pytest-android-2019.2a3 × × × pytest-annotate-1.0.3
pytest-ansible-2.2.2 pytest-ansible-playbook-0.4.1 pytest-ansible-playbook-runner-0.0.5
pytest-antilru-1.0.5 pytest-anything-0.1.1 × pytest-aoc-1.3a0 × × ×
pytest-apistellar-0.2.0 pytest-appengine-0.0.2 × × × pytest-appium-0.1
pytest-approvaltests-0.1.1 × × × pytest-arraydiff-0.3 pytest-asptest-0.1.0 × × ×
pytest-assert-utils-0.1.1 pytest-assertutil-0.0.2 pytest-assume-2.2.1
pytest-ast-back-to-python-1.2.1 × × × pytest-ast-transformer-1.0.3 pytest-astropy-0.8.0
pytest-astropy-header-0.1.2 pytest-async-mongodb-0.0.1.dev3 × × × pytest-asyncio-0.9.0 × × ×
pytest-asyncio-cooperative-0.4.1 × pytest-asyncio-network-simulator-0.1.0a2 pytest-atomic-2.0.0
pytest-attrib-0.1.3 × × × pytest-auto-parametrize-0.1.0 × × × pytest-autochecklog-0.2.0 × × ×
pytest-automock-0.7.0 pytest-avoidance-0.3.0 pytest-aws-0.1.0
pytest-axe-1.1.6 pytest-azurepipelines-1.0.0rc2 pytest-azurepipelines42–0.8.3
pytest-bandit-0.5.2 pytest-base-url-1.4.1 × × × pytest-bdd-3.3.0
pytest-bdd-splinter-0.4.0 pytest-bdd-web-0.1.1 pytest-bdd-wrappers-0.1.3 × × ×
pytest-beakerlib-0.7.1 pytest-beds-0.3.0 × × × pytest-bench-0.3.0
pytest-benchmark-3.2.3 pytest-bigchaindb-0.1.0 × × × pytest-black-0.3.9
pytest-black-multipy-1.0.0 pytest-blame-0.1.7 × × × pytest-blink1–0.0.8 × × ×
pytest-blockage-0.2.2 × × × pytest-blocker-0.2 pytest-board-0.1.0 × × ×
pytest-bpdb-0.1.4 pytest-breed-adapter-1.2.0 × × × pytest-browser-0.2.0
pytest-browsermob-proxy-0.1 pytest-browserstack-local-0.5.0 pytest-bug-0.8.0
pytest-bugzilla-0.2 × × × pytest-bugzilla-notifier-1.1.9 pytest-buildkite-0.2.0
pytest-bwrap-0.1 × × × pytest-cache-1.0 × × × pytest-cagoule-0.4.0
pytest-camel-collect-1.0.1 pytest-caprng-0.1.0 pytest-capture-deprecatedwarnings-0.2
pytest-capturelog-0.7 pytest-cases-1.9.3 × × × pytest-cassandra-0.1.0 × × ×
pytest-catch-server-1.0.0 pytest-catchlog-1.2.2 pytest-chalice-0.0.4
pytest-chdir-0.1.3 pytest-check-0.3.8 × × pytest-check-links-0.4.2 × × ×
pytest-check-mk-0.1.1 pytest-checkdocs-1.2.3 pytest-checkipdb-0.3.5
pytest-circleci-0.0.3 pytest-circleci-parallelized-0.0.4 pytest-ckan-0.0.12 × × ×
pytest-clarity-0.3.0a0 pytest-cldf-0.2.1 pytest-click-0.3
pytest-clld-1.0.2 pytest-cloud-4.0.0 × × × pytest-cobra-1.0.4 × ×
pytest-codecheckers-0.2 pytest-codestyle-2.0.1 pytest-colordots-1.1
pytest-common-subject-1.0.1 × × × pytest-concurrent-0.2.2 pytest-config-0.0.11 × × ×
pytest-console-scripts-0.2.0 pytest-consul-0.2.0 pytest-contextfixture-0.1.1
pytest-contexts-0.1.3 pytest-cookies-0.5.1 pytest-couchdbkit-0.5.1 × × ×
pytest-count-0.1.0 × × × pytest-cov-2.8.1 × × × pytest-cov-exclude-0.0.9
pytest-cover-3.0.0 × × × pytest-coverage-0.0.1 × × × pytest-cpp-1.2.0 × × ×
pytest-cram-0.2.0 × × × pytest-crate-0.3.0 pytest-cricri-1.0
pytest-crontab-0.1 pytest-csv-2.0.2 pytest-curio-0.1.0
pytest-curl-report-0.5.4 × × × pytest-custom-exit-code-0.3.0 pytest-custom-report-1.0.1
pytest-cython-0.1.0 × × × pytest-dash-2.1.2 × × × pytest-data-0.4
pytest-data-file-0.1 pytest-datadir-1.3.1 pytest-datadir-mgr-1.0.1
pytest-datadir-ng-1.1.1 pytest-datafiles-2.0 pytest-dataplugin-0.0.2
pytest-datarecorder-1.3.0 pytest-datatest-0.1.3 pytest-db-0.1
pytest-dbfixtures-1.0.0 × × × pytest-dbus-notification-1.0.1 × × × pytest-deadfixtures-2.2.0
pytest-demo-0.1.3 × × × pytest-dependency-0.5.1 pytest-depends-1.0.1
pytest-deprecate-1.0.2 pytest-describe-1.0.0 pytest-describe-it-0.1.0
pytest-devpi-server-1.7.0 pytest-diamond-0.0.1 × × × pytest-dicom-0.0.1a0
pytest-dictsdiff-0.5.8 pytest-diff-0.1.9 × × × pytest-diffeo-0.2.0 × × ×
pytest-disable-0.2 × × × pytest-disable-plugin-0.1.2 pytest-divide-and-cover-0.5.1
pytest-django-3.9.0 × × × pytest-django-ahead-3.0.0.2 pytest-django-casperjs-0.1.0 × × ×
pytest-django-dotenv-0.1.2 pytest-django-gcir-2.8.1 × × × pytest-django-haystack-0.3.0
pytest-django-ifactory-0.3.0 × × × pytest-django-lite-0.1.1 pytest-django-model-0.1.9
pytest-django-ordering-1.2.0 pytest-django-queries-1.1.0 pytest-django-rq-0.2.0 × × ×
pytest-django-sqlcounts-0.1.0 × × × pytest-django-testing-postgresql-0.1.post0 × × × pytest-djangoapp-0.9.0
pytest-djangorestframework-0.1.2 × × × pytest-doc-0.0.1 × × × pytest-docgen-1.3.0
pytest-docker-0.6.1 × × × pytest-docker-butla-0.7.0 × × × pytest-docker-compose-3.1.2
pytest-docker-db-1.0.4 pytest-docker-fixtures-1.3.7 pytest-docker-pexpect-0.9
pytest-docker-postgresql-0.1.0 pytest-docker-py-1.1.3 pytest-dockerc-1.0.7
pytest-docs-0.1.0 pytest-docstyle-2.0.1 pytest-doctest-custom-1.0.0
pytest-doctest-ellipsis-markers-0.1.0 pytest-doctest-import-0.1.1 pytest-doctest-ufunc-0.1.0
pytest-doctestplus-0.6.1 pytest-dolphin-0.3.9 × × × pytest-doorstop-0.1.0b0 × × ×
pytest-dotenv-0.4.0 pytest-drf-0.1.0 × × × pytest-drop-dup-tests-0.2.0
pytest-dump2json-0.1.0 pytest-dynamodb-2.0.1 pytest-easy-addoption-0.1.1
pytest-easy-api-0.0.3 pytest-easyread-0.1.0 pytest-ec2–0.1
pytest-echo-1.7.1 pytest-elasticsearch-2.0.1 pytest-elk-reporter-0.1.9
pytest-email-0.1 pytest-emoji-0.2.0 pytest-emoji-output-0.1.5 × × ×
pytest-enhancements-0.0.4 pytest-env-0.6.2 pytest-env-info-0.3.0 × × ×
pytest-env-yaml-0.2.0 × × × pytest-envfiles-0.1.0 pytest-envvars-1.0.0
pytest-eradicate-0.0.4 pytest-error-for-skips-2.0.2 pytest-ethereum-0.1.0a9 × × ×
pytest-eucalyptus-0.3.3 × pytest-excel-1.2.3 × × × pytest-exceptional-0.1.1
pytest-executable-0.4.0 pytest-expect-1.1.0 pytest-expecter-2.1b3
pytest-expectr-1.4.2 pytest-external-blockers-0.0.1 pytest-extra-durations-0.1.3
pytest-fabric-1.0.0 × × × pytest-factoryboy-2.0.3 pytest-failed-to-verify-0.1.5
pytest-faker-2.0.0 pytest-falcon-0.4.2 pytest-falcon-client-2.0.1
pytest-fantasy-0.2.15 pytest-fastest-0.0.9 × × × pytest-faulthandler-2.0.1
pytest-fauxfactory-1.1.1 pytest-figleaf-1.0 × × × pytest-filedata-0.4.0
pytest-filter-case-1.2.0 pytest-filter-subpackage-0.1.1 pytest-finer-verdicts-1.0.6
pytest-firefox-0.1.1 pytest-fixture-config-1.7.0 pytest-fixture-marker-0.1.0
pytest-fixture-order-0.1.2 × × × pytest-fixture-tools-1.0.0 pytest-fixtures-0.1.0
pytest-flake8–1.0.5 pytest-flake8dir-2.2.0 pytest-flakefinder-0.1.3
pytest-flakes-4.0.0 × × × pytest-flaptastic-0.0.25 pytest-flask-1.0.0
pytest-flask-sqlalchemy-1.0.2 pytest-flask-sqlalchemy-transactions-1.0.2 pytest-focus-0.1.1 × × ×
pytest-forcefail-0.0.0.3 pytest-forked-1.1.3 pytest-freeze-reqs-0.1.8 × × ×
pytest-freezegun-0.4.1 pytest-func-cov-0.1.3 pytest-fxa-1.4.0 × × ×
pytest-gc-0.0.1 pytest-gcov-0.0.1-alpha × × × pytest-gevent-1.1.0
pytest-gherkin-0.1.7 pytest-ghostinspector-0.4.0 × × × pytest-girder-3.1.1.dev66
pytest-git-1.7.0 pytest-gitcov-0.1.5 × × × pytest-github-0.3.1
pytest-gitignore-1.3 pytest-graphql-schema-0.0.2 pytest-greendots-0.3
pytest-growl-0.2 × × × pytest-grpc-0.8.0 × × × pytest-hammertime-1.1.1
pytest-harvest-1.9.1 × × × pytest-helper-0.2.2 pytest-helpers-namespace-2019.1.8
pytest-hidecaptured-0.2.2 pytest-historic-0.1.2 pytest-historic-hook-0.1.2
pytest-honors-0.1.5 pytest-hoverfly-wrapper-0.1.3 pytest-html-2.1.1
pytest-html-profiling-1.0.0 × × × pytest-http-0.1 pytest-http-mocker-0.0.1
pytest-httpbin-1.0.0 pytest-httpretty-0.2.0 × × × pytest-httpserver-0.3.4
pytest-httpx-0.2.1 pytest-hue-1.0.0 × × × pytest-hypo-25–2.3
pytest-icdiff-0.5 pytest-idapro-0.4.3 × × × pytest-ignore-flaky-1.0.0
pytest-incremental-0.5.0 × × × pytest-influxdb-0.0.27 pytest-info-collector-0.3
pytest-informative-node-1.0.0 × × × pytest-infrastructure-0.0.2 × pytest-inmanta-1.0.0
pytest-inmanta-extensions-2020.2 pytest-instafail-0.4.1.post0 pytest-instrument-0.3.1
pytest-integration-0.2.2 pytest-interactive-0.1.4 pytest-invenio-1.3.0
pytest-involve-0.1.4 pytest-ipdb-0.1-prerelease2 pytest-ipynb-1.1.1
pytest-isort-1.0.0 pytest-it-0.1.4 pytest-iterassert-0.0.2
pytest-jasmine-0.0.2 pytest-jest-0.3.0 pytest-jira-0.3.0 × × ×
pytest-jobserver-1.0.0 pytest-joke-0.1.1 pytest-json-0.4.0
pytest-json-report-1.2.1 × × × pytest-jsonlint-0.0.1 pytest-kafka-0.4.0
pytest-kind-20.5.3 × pytest-knows-0.1.5 × × × pytest-konira-0.2 × × ×
pytest-krtech-common-0.1.35 × pytest-lambda-1.1.0 × × × pytest-lamp-0.1.0
pytest-layab-1.3.0 pytest-lazy-fixture-0.6.3 pytest-leaks-0.3.1
pytest-level-0.1.3 pytest-libfaketime-0.1.2 pytest-libnotify-1.0.0
pytest-ligo-0.1.3 × pytest-listener-1.7.0 × × × pytest-litf-0.1.5
pytest-live-0.6 pytest-localftpserver-1.0.1 pytest-localserver-0.5.0
pytest-localstack-0.4.1 pytest-log-report-1.0.1 × × × pytest-logbook-1.2.0
pytest-logfest-0.3.0 pytest-logger-0.5.1 × × × pytest-logging-2015.11.4
pytest-manual-marker-0.1 pytest-mark-no-py3–0.1.0 pytest-marker-bugzilla-0.9.1.dev6 × × ×
pytest-markers-presence-0.8.0 × pytest-markfiltration-0.8 × × × pytest-marks-0.4 × × ×
pytest-match-skip-0.2.1 pytest-matcher-1.3.5 pytest-mccabe-1.0
pytest-md-0.2.0 pytest-md-report-0.0.2 × × × pytest-memprof-0.2.0
pytest-menu-0.0.4 pytest-metadata-1.9.0 pytest-metrics-0.4
pytest-mimesis-1.1.0 pytest-ml-0.1.0 × × × pytest-mocha-0.4.0
pytest-mock-3.1.0 pytest-mock-api-0.1.0 pytest-mock-helper-0.2.1 × × ×
pytest-mock-resources-1.2.2 pytest-mock-server-0.2.0 pytest-mockito-0.0.4
pytest-mockredis-0.1.6 pytest-mockservers-0.6.0 pytest-modifyjunit-1.5.post0
pytest-modifyscope-0.3.0 pytest-molecule-1.2.5 pytest-mongo-2.0.0
pytest-mongodb-2.2.0 pytest-monitor-1.2.0 pytest-monkeyplus-1.1.0 × × ×
pytest-monkeytype-1.0.5 pytest-moto-0.2.0 pytest-mozwebqa-2.0 × × ×
pytest-mp-0.0.4 pytest-mpi-0.3 × × × pytest-mpl-0.9 × × ×
pytest-mproc-3.2.3 × pytest-multihost-3.4 pytest-multilog-1.0.1
pytest-mutagen-1.0.5 pytest-mypy-0.6.2 × × × pytest-mypy-plugins-1.3.0
pytest-mypy-testing-0.0.7 × × pytest-mypyd-0.3.5 pytest-mysql-2.0.1
pytest-needle-0.3.8 pytest-neo-0.2.1 × × × pytest-network-0.0.1
pytest-nginx-1.1 pytest-nginx-iplweb-1.1.1 pytest-ngrok-0.0.3
pytest-ngsfixtures-0.8.1 pytest-nice-0.1.0 × × × pytest-nocustom-1.0 × × ×
pytest-nodev-1.0.1 × × × pytest-notebook-0.6.0 pytest-notification-0.1.0
pytest-notifier-1.0.3 pytest-notimplemented-1.0.0 × × × pytest-notion-1.0.1
pytest-nunit-0.5.3 pytest-ochrus-0.0.6 × × × pytest-odoo-0.5.0 × × ×
pytest-odoo-fixtures-0.1.dev0 × × × pytest-oerp-0.2.0 pytest-ok-2019.4.1
pytest-only-1.2.2 pytest-oot-0.6.1 × × × pytest-openfiles-0.5.0
pytest-opentmi-0.1.2 × × × pytest-optional-0.0.3 pytest-optional-tests-0.1.1
pytest-orchestration-0.0.9 pytest-ordering-0.6 pytest-osxnotify-0.1.7 × × ×
pytest-pact-0.0.0 pytest-parallel-0.1.0 pytest-param-0.1.1
pytest-paramark-0.1.1 × × × pytest-parametrization-2019.1.4 pytest-parametrized-1.2
pytest-parawtf-1.0.2 pytest-pass-1.1 pytest-paste-config-0.1
pytest-pdb-0.3.1 × × × pytest-pdb-break-0.0.8 pytest-peach-1.5.9
pytest-pep257–0.0.5 pytest-pep8–1.0.6 × × × pytest-percent-0.0.4 ×
pytest-pgsql-1.1.1 × × × pytest-picked-0.4.4 pytest-pigeonhole-0.3.1
pytest-pikachu-0.1.0 pytest-pilot-0.1.2 × × × pytest-pings-0.1.0 ×
pytest-pinpoint-0.2.0 pytest-pipeline-0.3.0 pytest-platform-markers-1.0.0
pytest-play-2.3.1 pytest-plt-1.0.1 pytest-plugin-helpers-0.1.0
pytest-plus-0.2 pytest-pmisc-1.0.2 × pytest-polarion-cfme-0.1.5
pytest-polarion-collect-0.9 pytest-polecat-0.0.2 × × × pytest-ponyorm-0.3.3 × × ×
pytest-poo-0.2 pytest-poo-fail-1.1 pytest-pop-1.1 × × ×
pytest-postgres-0.7.0 pytest-postgresql-2.3.0 pytest-pride-0.1.2
pytest-print-0.1.3 pytest-profiling-1.7.0 pytest-progress-1.2.1 × × ×
pytest-prometheus-0.1 pytest-prosper-1.0.0 pytest-pspec-0.0.3
pytest-pudb-0.7.0 pytest-purkinje-0.1.6 × × × pytest-pycharm-0.6.0
pytest-pycodestyle-2.1.3 pytest-pydev-0.1.1 pytest-pydocstyle-2.1.3
pytest-pylint-0.17.0 pytest-pypi-0.1.1 pytest-pypom-navigation-2.0.3
pytest-pyq-1.2.0 pytest-pyramid-server-1.7.0 pytest-pytestrail-0.9.3 × × ×
pytest-pythonpath-0.7.3 pytest-qt-3.3.0 × × × pytest-qt-app-1.0.1 × × ×
pytest-quarantine-2.0.0a0 pytest-quickcheck-0.8.4 pytest-rabbitmq-2.0.1
pytest-race-0.1.1 pytest-rage-0.1 pytest-raises-0.11
pytest-raisesregexp-2.1 pytest-raisin-0.3 pytest-random-0.02
pytest-random-order-1.0.4 pytest-randomly-3.3.1 pytest-randomness-0.1.0
pytest-readme-1.0.0 pytest-reana-0.6.0 pytest-recording-0.7.0
pytest-redis-2.0.0 pytest-redmine-0.0.1 × × × pytest-ref-0.2.0
pytest-reference-formatter-0.2 pytest-regressions-2.0.0 pytest-regtest-1.4.4
pytest-relaxed-1.1.5 pytest-remfiles-0.0.2 pytest-remotedata-0.3.2
pytest-remove-stale-bytecode-5.0.1 pytest-reorder-0.1.1 pytest-repeat-0.8.0
pytest-replay-1.2.0 pytest-repo-health-0.1.0 pytest-report-0.2.1
pytest-report-parameters-0.5.0 pytest-reporter-0.2.0 pytest-reporter-html1–0.2.0
pytest-reportinfra-0.1 pytest-reporting-0.1.0 pytest-reportlog-0.1.1
pytest-reportportal-5.0.3 pytest-reqs-0.2.1 × × × pytest-requests-0.3.0 × × ×
pytest-rerun-0.0.1 × × × pytest-rerunfailures-9.0 pytest-resilient-circuits-35.0.203
pytest-resource-0.1.3 pytest-resource-path-1.0.0 pytest-responsemock-0.2.0
pytest-responses-0.4.0 pytest-restrict-3.1.0 pytest-rethinkdb-0.1.3 × × ×
pytest-reverse-1.0.1 pytest-ringo-0.2.3 pytest-rng-1.0.0
pytest-rotest-0.1.0 × × × pytest-rpc-1.1.1 pytest-rt-1.0.0 × × ×
pytest-runfailed-0.6 pytest-runner-5.2 × × pytest-sa-pg-0.3.2
pytest-salt-2020.1.27 × × × pytest-salt-containers-0.2.dev9 × × × pytest-salt-factories-0.10.4 × × ×
pytest-salt-from-filenames-2019.1.30.post3 pytest-salt-runtests-bridge-2019.7.10 pytest-sanic-1.6.1 × × ×
pytest-scenario-1.0a6 pytest-scope-modifying-0.1.0 pytest-securestore-0.1.3
pytest-select-0.1.2 × × × pytest-selenium-1.9.1 × × × pytest-selenium-enhancer-1.5.1
pytest-selenium-pdiff-0.4.0 pytest-seleniumbase-0.2.0 pytest-send-email-0.1 × × ×
pytest-sentry-0.1.1 pytest-server-fixtures-1.7.0 pytest-serverless-0.10.1
pytest-services-2.0.1 × × × pytest-session-fixture-globalize-0.0.0.3 pytest-session2file-0.1.9 × × ×
pytest-session_to_file-0.1.2 × × × pytest-sftpserver-1.3.0 pytest-shard-0.1.1
pytest-shell-0.2.3 pytest-sheraf-0.1.1 × × × pytest-sherlock-0.1.6 × × ×
pytest-shutil-1.7.0 pytest-simple-plugin-0.0.4 pytest-simple-settings-0.1.4
pytest-single-file-logging-0.1.18 pytest-skipper-0.1.3 × × × pytest-skippy-0.1.1 × × ×
pytest-slack-2.1.2 pytest-smartcollect-1.0.2 pytest-smartcov-0.3
pytest-snail-0.1.0 pytest-snapci-0.1.4 pytest-snapshot-0.4.0
pytest-socket-0.3.4 pytest-soft-assertions-0.1.2 × pytest-solr-1.0a1
pytest-sorter-0.1.8 × × × pytest-sourceorder-0.5.1 pytest-spark-0.6.0
pytest-spawner-0.1.0 pytest-spec-2.0.0 pytest-sphinx-0.2.2
pytest-spiratest-1.0.0 × × × pytest-splinter-2.0.1 × × × pytest-split-0.1.5
pytest-split-tests-1.0.9 pytest-splunk-addon-1.1.0 × pytest-sqitch-0.1.1
pytest-sql-bigquery-0.0.3 × pytest-sqlalchemy-0.2.1 pytest-ssh-0.1
pytest-start-from-0.1.0 pytest-statsd-1.1.6 pytest-steps-1.7.3 × × ×
pytest-stepwise-0.4 pytest-stoq-0.7.0 × × × pytest-stress-1.0.1
pytest-structlog-0.2 pytest-structmpd-0.1.0 pytest-stub-1.1.0
pytest-stubprocess-0.1.0 pytest-study-0.14 pytest-subprocess-0.1.4
pytest-subtesthack-0.1.1 pytest-subtests-0.3.0 pytest-subunit-0.0.10
pytest-sugar-0.9.3 pytest-sugar-bugfix159–1.0 × × × pytest-super-check-2.1.0
pytest-svn-1.7.0 pytest-symbols-0.1.1 pytest-tap-3.1
pytest-tblineinfo-0.4 pytest-teamcity-logblock-0.0.0.2 pytest-tempdir-2019.10.12
pytest-terraform-0.1.0 × × × pytest-terraform-fixture-0.1.2 × × × pytest-test-groups-1.0.3
pytest-test-this-0.3.0 pytest-testbook-0.0.9 pytest-testconfig-0.2.0
pytest-testdirectory-3.1.0 pytest-testdox-1.2.1 pytest-testlink-adaptor-0.33
pytest-testmon-1.0.2 pytest-testobject-1.0.0 pytest-testrail-2.8.3
pytest-testrail-e2e-2.2.1 pytest-testrail-plugin-0.0.3 × pytest-testrail-reporter-1.9 × × ×
pytest-testrail2–2.7.9 pytest-tesults-1.1.0 pytest-tezos-0.1.3
pytest-thawgun-0.0.1 pytest-threadleak-0.2.0 pytest-timeit-0.3.0 × × ×
pytest-timeout-1.3.4 pytest-timeouts-1.2.1 pytest-timer-0.0.8
pytest-tipsi-django-2.4.0 pytest-tipsi-testing-1.3.1 pytest-tldr-0.2.1
pytest-todo-0.2.1 pytest-tomato-1.0.9 pytest-toolbelt-0.1.0 × × ×
pytest-toolbox-0.4 pytest-tornado-0.8.0 pytest-tornado-yen3–0.5.2
pytest-tornado5–2.0.0 pytest-tornasync-0.6.0.post2 pytest-track-0.1.1
pytest-translations-2.0.0 pytest-travis-fold-1.3.0 pytest-trello-0.0.7 × × ×
pytest-trepan-2.0.0 × × × pytest-trialtemp-1.0.1 pytest-trio-0.5.2
pytest-tstcls-2020.1.1 pytest-twisted-1.12 pytest-ubersmith-1.2.0 × × ×
pytest-ui-0.3.6b0 pytest-unittest-filter-0.2.1 pytest-unmarked-1.3
pytest-unordered-0.1.0 pytest-vagrant-2.0.3 pytest-valgrind-0.1.0 × × ×
pytest-variables-1.9.0 pytest-vcr-1.0.2 pytest-vcrpandas-0.0.2 × × ×
pytest-venv-0.2 × × pytest-verbose-parametrize-1.7.0 pytest-virtualenv-1.7.0
pytest-voluptuous-1.1.0 pytest-vts-0.4.8 pytest-vw-0.1.0
pytest-wa-e2e-plugin-0.1.3 pytest-warnings-0.3.0 pytest-watch-4.2.0
pytest-wdl-1.4.0 pytest-web-ui-1.0.0 pytest-webdriver-1.7.0
pytest-wetest-1.0.1 pytest-wholenodeid-0.2 pytest-winnotify-0.4.1 × × ×
pytest-workflow-1.3.0 pytest-xdist-1.32.0 pytest-xdist-debug-for-graingert-0.0.1 × × ×
pytest-xdist-forked-0.0.1 × × × pytest-xfiles-0.2.0 pytest-xpara-0.1.1
pytest-xprocess-0.13.1 pytest-xray-0.2.1 pytest-xrayjira-1.0.2 × × ×
pytest-xvfb-1.2.0 × × × pytest-yaml-1.2.1 pytest-yamltree-0.1.2
pytest-yamlwsgi-0.6 × × × pytest-yapf-0.1.1 pytest-yapf3–0.5.1
pytest-yield-1.0.0 × × × pytest-zafira-1.0.3 × × × pytest-zap-0.2 × × ×
pytest-zigzag-1.1.1 × × pytest_cid-1.0.0 × × pytest_docker_tools-0.2.0 × × ×
pytest_gae-0.2.4 pytest_markdown-0.0.7 × × × pytest_matrix-0.6
python-clu-0.7.1 × × × python-hiptest-0.0.0 pytoolsc2–0.2 × × ×
qase-pytest-1.0.0a3 qieman-xdist-0.6 querify-0.1.0
recommender-engine-0.3.0 regress-0.0.4 responsysrest-0.1.9
rigor-0.5.9 × × × sat-search-0.3.0b2 schemathesis-1.5.1
scrapy-mock-0.1.0 × seaworthy-0.4.2 seleniumbase-1.37.9
sentinel-s3–0.8.0 × × × sg-test-env-0.0.1 sg-testing-0.0.30
shield34–1.0.257 × × × sipster-0.0.1 slacktools-slackfixtures-1.0.0
slim-0.0.4 snapshottest-0.5.1 specd-0.2.8 × × ×
stestdata-0.1.0 syrupy-0.4.2 takeltest-0.2.0 × × ×
tasting-0.0.1 tavern-1.0.0a3 tavern-extended-0.9.1 × × ×
tavern-grpc-0.30.11 test-exe-matrix-0.1.5 testaid-0.9.7 × × ×
teste-do-noia-0.2 testinfra-5.0.0 × × × tidypy-0.9.0
transfer-learning-0.2 × × type-comparable-0.4 unittest-sheraf-0.1.0
unmock-0.1.2 uw-webdriver-recorder-1.0.9 websauna.tests-1.0a8 × ×
wikilink-0.3.0.post1 worldmor-0.3.1 xdoctest-0.9.1
xparser-0.0.4rc0 × × × xthematic-0.0.3 yaop-0.1.1
zalando-cli-bundle-20.3.0
Poznámka: podrobnější seznamy lze najít na adrese http://plugincompat.herokuapp.com/.

5. Jednoduchý projekt ukazující základní možnosti nástroje pytest

Nyní se podívejme na zdrojový kód velmi jednoduchého projektu, na němž si postupně ukážeme jednotlivé možnosti poskytované nástrojem pytest. Základní skript se jmenuje primes.py a obsahuje implementaci výpočtu prvočísel s využitím starodávného algoritmu známého pod jménem Eratosthenovo síto. Konkrétní poměrně rychlá implementace byla převzata ze známého serveru Rosetta Code:

"""Výpočet seznamu prvočísel až do zadaného limitu."""
 
# originální kód lze nalézt na adrese:
# http://www.rosettacode.org/wiki/Sieve_of_Eratosthenes#Odds-only_version_of_the_array_sieve_above
 
 
def primes2(limit):
    """Výpočet seznamu prvočísel až do zadaného limitu."""
    # okrajový případ
    if limit < 2:
        return []
 
    # druhý případ - 2 je speciálním prvočíslem
    if limit < 3:
        return [2]
 
    lmtbf = (limit - 3) // 2
 
    # naplnění tabulky, která se bude prosívat
    buf = [True] * (lmtbf + 1)
 
    # vlastní prosívání
    for i in range((int(limit ** 0.5) - 3) // 2 + 1):
        if buf[i]:
            p = i + i + 3
            s = p * (i + 1) + i
            buf[s::p] = [False] * ((lmtbf - s) // p + 1)
 
    # vytvoření seznamu prvočísel
    return [2] + [i + i + 3 for i, v in enumerate(buf) if v]

Skript main.py je spustitelný – pouze vypočte a zobrazí prvočísla v rozsahu 0..100:

#!/usr/bin/env python3
 
"""Vstupní bod do testované aplikace."""
 
from primes import *
 
 
if __name__ == '__main__':
    # pouze se ujistíme, že lze spustit funkci primes2
    print(primes2(100))

A konečně soubor test_primes.py obsahuje prozatím velmi jednoduchou implementaci jednotkových testů. Povšimněte si, že se v testech přímo používá klíčové slovo assert – pokud funkce neskončí s výjimkou, je test vyhodnocen jako pass, v odlišném případě je hlášena chyba:

"""Implementace jednotkových testů."""
 
from primes import primes2
 
 
def test_primes_10():
    """Otestování výpočtu seznamu prvočísel až do limitu 10."""
    # získat seznam prvočísel až do limitu 10
    p = primes2(10)
    # testy lze dále rozšiřovat
    assert 2 in p
    assert 10 not in p
Poznámka: důležité je, že jméno posledního souboru začíná předponou „test_“, protože právě s využitím této části názvu hledá pytest ty skripty, které obsahují jednotkové testy. Totéž platí i pro funkci s implementací jednotkového testu – taktéž by měla začínat předponou test_.

6. Spuštění nástroje pytest s vyhodnocením výsledků

V adresáři s demonstračním projektem postačuje spustit příkaz pytest. Nejprve si vyzkoušíme, co se stane ve chvíli, kdy nepoužijeme žádné další parametry:

$ pytest

Měla by se zobrazit (mj.) následující tabulka s celkovými výsledky testů:

============================= test session starts ==============================
platform linux -- Python 3.6.6, pytest-5.4.2, py-1.5.2, pluggy-0.13.1
rootdir: /home/ptisnovs/src/python/testing-in-python/pytest/primes1
plugins: voluptuous-1.0.2, cov-2.5.1
collected 1 item
 
test_primes.py .                                                         [100%]
 
============================== 1 passed in 0.02s ===============================
Poznámka: alternativně lze použít i python3 -m pytest. Rozdíly mezi oběma možnostmi si vysvětlíme příště.

Pokud budeme chtít zobrazit, které konkrétní testy se spouští, pomůže nám „ukecaný“ (verbose) režim:

$ pytest -v

Ve výsledcích se nyní objeví informace o jednotlivých funkcích a metodách s jednotkovými testy, které jsou spouštěny:

============================= test session starts ==============================
platform linux -- Python 3.6.6, pytest-5.4.2, py-1.5.2, pluggy-0.13.1 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: /home/ptisnovs/src/python/testing-in-python/pytest/primes1
plugins: voluptuous-1.0.2, cov-2.5.1
collected 1 item
 
test_primes.py::test_primes_10 PASSED                                    [100%]
 
============================== 1 passed in 0.02s ===============================

Obrázek 1: Zobrazení na terminálu s podporou barevného výstupu.

Nyní algoritmus výpočtu schválně poškodíme – vynecháme přidání čísla 2 (které je jediným sudým prvočíslem a tudíž se s ním pracuje odlišně):

# vytvoření seznamu prvočísel
return [i + i + 3 for i, v in enumerate(buf) if v]

Jednotkové testy by nyní měly podle očekávání zhavarovat:

$ pytest -v
 
=================================== FAILURES ===================================
________________________________ test_primes_10 ________________________________
 
    def test_primes_10():
        """Otestování výpočtu seznamu prvočísel až do limitu 10."""
        # získat seznam prvočísel až do limitu 10
        p = primes2(10)
        # testy lze dále rozšiřovat
>       assert 2 in p
E       assert 2 in [3, 5, 7]
 
test_primes.py:11: AssertionError
=========================== short test summary info ============================
FAILED test_primes.py::test_primes_10 - assert 2 in [3, 5, 7]
============================== 1 failed in 0.08s ===============================

Obrázek 2: Zobrazení na terminálu s podporou barevného výstupu.

Další možností je spuštění s parametrem -rA, které pod testy zobrazí ještě krátké celkové zhodnocení, což je výhodné zejména u projektů s mnoha stovkami testů:

$ pytest -rA
 
============================= test session starts ==============================
platform linux -- Python 3.6.6, pytest-5.4.2, py-1.5.2, pluggy-0.13.1
benchmark: 3.2.3 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000)
rootdir: /home/ptisnovs/src/python/testing-in-python/pytest/primes1
plugins: voluptuous-1.0.2, benchmark-3.2.3, cov-2.5.1
collected 1 item
 
test_primes.py .                                                         [100%]
 
==================================== PASSES ====================================
=========================== short test summary info ============================
PASSED test_primes.py::test_primes_10
============================== 1 passed in 0.02s ===============================

7. Instalace a použití nového pluginu

Pluginy, například ty zmíněné ve čtvrté kapitole, se instalují stejným způsobem, jako jakýkoli jiný balíček určený pro programovací jazyk Python. Pokud například používáte nástroj pip nebo pip3 (podle aktuálního nastavení operačního systému), může instalace dále popsaného pluginu pytest-benchmark vypadat následovně:

$ pip3 install --user pytest-benchmark
Poznámka: přepínač –user zajistí lokální instalaci pouze pro aktuálně přihlášeného uživatele (do jeho domovského adresáře). Pochopitelně je taktéž možné použít virtuální prostředí Pythonu atd.

8. Ladění v případě, že dochází k problémům při inicializaci pluginu

V případě, že při načítání pluginů nebo při hledání testů dochází k problémům (většinou dosti záhadným), může pomoci spustit nástroj pytest s přepínačem –trace-config, tedy například následujícím způsobem:

$ pytest --trace-config

Výsledek může vypadat například následovně (povšimněte si, že většina balíčků byla nainstalována pouze lokálně):

PLUGIN registered: <_pytest.config.PytestPluginManager object at 0x7f909fbba630>
PLUGIN registered: <_pytest.config.Config object at 0x7f909f3c4e48>
PLUGIN registered: <module '_pytest.mark' from '/home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/mark.py'>
PLUGIN registered: <module '_pytest.main' from '/home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/main.py'>
PLUGIN registered: <module '_pytest.terminal' from '/home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/terminal.py'>
PLUGIN registered: <module '_pytest.runner' from '/home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/runner.py'>
PLUGIN registered: <module '_pytest.python' from '/home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/python.py'>
PLUGIN registered: <module '_pytest.pdb' from '/home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/pdb.py'>
PLUGIN registered: <module '_pytest.unittest' from '/home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/unittest.py'>
PLUGIN registered: <module '_pytest.capture' from '/home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/capture.py'>
PLUGIN registered: <module '_pytest.skipping' from '/home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/skipping.py'>
PLUGIN registered: <module '_pytest.tmpdir' from '/home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/tmpdir.py'>
PLUGIN registered: <module '_pytest.monkeypatch' from '/home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/monkeypatch.py'>
PLUGIN registered: <module '_pytest.recwarn' from '/home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/recwarn.py'>
PLUGIN registered: <module '_pytest.pastebin' from '/home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/pastebin.py'>
PLUGIN registered: <module '_pytest.helpconfig' from '/home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/helpconfig.py'>
PLUGIN registered: <module '_pytest.nose' from '/home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/nose.py'>
PLUGIN registered: <module '_pytest.assertion' from '/home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/assertion/__init__.py'>
PLUGIN registered: <module '_pytest.genscript' from '/home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/genscript.py'>
PLUGIN registered: <module '_pytest.junitxml' from '/home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/junitxml.py'>
PLUGIN registered: <module '_pytest.resultlog' from '/home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/resultlog.py'>
PLUGIN registered: <module '_pytest.doctest' from '/home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/doctest.py'>
PLUGIN registered: <module '_pytest.cacheprovider' from '/home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/cacheprovider.py'>
PLUGIN registered: <module 'pytest_voluptuous.plugin' from '/home/ptisnovs/.local/lib/python3.6/site-packages/pytest_voluptuous/plugin.py'>
PLUGIN registered: <module 'pytest_cov.plugin' from '/usr/lib/python3.6/site-packages/pytest_cov/plugin.py'>
PLUGIN registered: <_pytest.capture.CaptureManager object at 0x7f909aee1390>
PLUGIN registered: <Session 'pytest'>
PLUGIN registered: <_pytest.cacheprovider.LFPlugin object at 0x7f909aef1da0>
PLUGIN registered: <_pytest.terminal.TerminalReporter object at 0x7f909ac7ab38>
PLUGIN registered: <_pytest.python.FixtureManager object at 0x7f909ac2bcf8>
============================= test session starts ==============================
platform linux -- Python 3.6.6, pytest-2.9.2, py-1.5.2, pluggy-0.3.1
using: pytest-2.9.2 pylib-1.5.2
setuptools registered plugins:
  pytest-voluptuous-1.0.2 at /home/ptisnovs/.local/lib/python3.6/site-packages/pytest_voluptuous/plugin.py
  pytest-cov-2.5.1 at /usr/lib/python3.6/site-packages/pytest_cov/plugin.py
active plugins:
    140259131893296     : <_pytest.config.PytestPluginManager object at 0x7f909fbba630>
    pytestconfig        : <_pytest.config.Config object at 0x7f909f3c4e48>
    mark                : /home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/mark.py
    main                : /home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/main.py
    terminal            : /home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/terminal.py
    runner              : /home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/runner.py
    python              : /home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/python.py
    pdb                 : /home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/pdb.py
    unittest            : /home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/unittest.py
    capture             : /home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/capture.py
    skipping            : /home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/skipping.py
    tmpdir              : /home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/tmpdir.py
    monkeypatch         : /home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/monkeypatch.py
    recwarn             : /home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/recwarn.py
    pastebin            : /home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/pastebin.py
    helpconfig          : /home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/helpconfig.py
    nose                : /home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/nose.py
    assertion           : /home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/assertion/__init__.py
    genscript           : /home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/genscript.py
    junitxml            : /home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/junitxml.py
    resultlog           : /home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/resultlog.py
    doctest             : /home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/doctest.py
    cacheprovider       : /home/ptisnovs/.local/lib/python3.6/site-packages/_pytest/cacheprovider.py
    pytest_voluptuous   : /home/ptisnovs/.local/lib/python3.6/site-packages/pytest_voluptuous/plugin.py
    pytest_cov          : /usr/lib/python3.6/site-packages/pytest_cov/plugin.py
    capturemanager      : <_pytest.capture.CaptureManager object at 0x7f909aee1390>
    session             : <Session 'pytest'>
    lfplugin            : <_pytest.cacheprovider.LFPlugin object at 0x7f909aef1da0>
    terminalreporter    : <_pytest.terminal.TerminalReporter object at 0x7f909ac7ab38>
    funcmanage          : <_pytest.python.FixtureManager object at 0x7f909ac2bcf8>
rootdir: /home/ptisnovs/src/python/testing-in-python/pytest, inifile:
plugins: voluptuous-1.0.2, cov-2.5.1

9. Zjištění pokrytí zdrojového kódu jednotkovými testy

Velmi užitečným pluginem pro nástroj pytest je plugin nazvaný pytest-cov (lze pochopitelně nalézt na PyPi). Tento plugin nainstalujeme jednoduše:

$ pip3 install --user pytest-cov

Po úspěšné instalaci můžeme přidat přepínač –cov, jehož hodnotou je jméno balíčku, popř. modulu(ů), které jsou testovány, tj. ty zdrojové kódy, u kterých potřebujeme znát pokrytí jednotkovými testy. Nejprve provedeme spuštění pro všechny skripty v aktuálním balíčku (zde adresáři):

pytest --cov=.

Ve výsledku s procenty pokrytí jednotkovými testy se v tomto případě objeví všechny skripty:

============================= test session starts ==============================
platform linux -- Python 3.6.6, pytest-5.4.2, py-1.5.2, pluggy-0.13.1
rootdir: /home/ptisnovs/src/python/testing-in-python/pytest/primes1
plugins: voluptuous-1.0.2, cov-2.5.1
collected 1 item
 
test_primes.py .                                                         [100%]
 
----------- coverage: platform linux, python 3.6.6-final-0 -----------
Name             Stmts   Miss  Cover
------------------------------------
main.py              4      4     0%
primes.py           13      2    85%
test_primes.py       5      0   100%
------------------------------------
TOTAL               22      6    73%
 
 
============================== 1 passed in 0.04s ===============================

Lepší a korektnější je specifikovat pouze jediný skript (bez uvedení koncovky):

$ pytest --cov=primes

Nyní se ve výsledku objeví skutečně pouze vlastní testovaný kód a nic dalšího:

============================= test session starts ==============================
platform linux -- Python 3.6.6, pytest-5.4.2, py-1.5.2, pluggy-0.13.1
rootdir: /home/ptisnovs/src/python/testing-in-python/pytest/primes1
plugins: voluptuous-1.0.2, cov-2.5.1
collected 1 item
 
test_primes.py .                                                         [100%]
 
----------- coverage: platform linux, python 3.6.6-final-0 -----------
Name        Stmts   Miss  Cover
-------------------------------
primes.py      13      2    85%
 
 
============================== 1 passed in 0.04s ===============================

10. Podrobnější výsledky pokrytí zdrojového kódu jednotkovými testy

Informace o tom, kolik procent zdrojového kódu je pokryto (skutečně vykonáno) v jednotkových testech, je sice užitečná, ovšem ještě lepší budou podrobnější informace o těch řádcích ve zdrojovém kódu, které naopak vyhodnoceny nebyly. I tyto informace je pochopitelně možné získat, a to velmi snadným způsobem:

$ pytest --cov=primes --cov-report term-missing

Nyní by se ve výpisu měly objevit i jednotlivé řádky, které nebyly v rámci jednotkových testů zavolány:

============================= test session starts ==============================
platform linux -- Python 3.6.6, pytest-5.4.2, py-1.5.2, pluggy-0.13.1
rootdir: /home/ptisnovs/src/python/testing-in-python/pytest/primes1
plugins: voluptuous-1.0.2, cov-2.5.1
collected 1 item
 
test_primes.py .                                                         [100%]
 
----------- coverage: platform linux, python 3.6.6-final-0 -----------
Name        Stmts   Miss  Cover   Missing
-----------------------------------------
primes.py      13      2    85%   11, 15
 
 
============================== 1 passed in 0.04s ===============================

Dokonce je možné si nechat vygenerovat HTML stránku (a několik dalších souborů s kaskádovými styly a JavaScriptem) s podrobnějšími informacemi o pokrytí zdrojových kódů testy:

$ pytest --cov=primes --cov-report html

Obrázek 3: Podrobnější výsledky s pokrytím zdrojového kódu jednotkovými testy. Nyní je zcela jasně patrné, které části je potřeba více otestovat.

11. Doplnění jedné chybějící větve do jednotkových testů

Z předchozího výsledku testů je zřejmé, že nám chybí doplnit testy pro dvě podmínky vyhodnocované na začátku výpočtu sekvence prvočísel. Pokusme se tedy doplnit test, zda se v prázdném rozsahu 0..0 nenachází žádné prvočíslo. Nový test je implementován ve funkci pojmenované test_primes0:

"""Implementace jednotkových testů."""
 
from primes import primes2
 
 
def test_primes_10():
    """Otestování výpočtu seznamu prvočísel až do limitu 10."""
    # získat seznam prvočísel až do limitu 10
    p = primes2(10)
    # testy lze dále rozšiřovat
    assert 2 in p
    assert 10 not in p
 
 
def test_primes_0():
    """Otestování výpočtu seznamu prvočísel do limitu 0."""
    p = primes2(0)
    # otestujeme, zda je sekvence prázdná (není zcela přesné)
    assert not p

Nové spuštění testů by mělo odhalit, že z celého výpočtu zůstal neotestován jen jediný programový řádek:

============================= test session starts ==============================
platform linux -- Python 3.6.6, pytest-5.4.2, py-1.5.2, pluggy-0.13.1
rootdir: /home/ptisnovs/src/python/testing-in-python/pytest/primes2
plugins: voluptuous-1.0.2, cov-2.5.1
collected 2 items
 
test_primes.py ..                                                        [100%]
 
----------- coverage: platform linux, python 3.6.6-final-0 -----------
Name        Stmts   Miss  Cover   Missing
-----------------------------------------
primes.py      13      1    92%   15
 
 
============================== 2 passed in 0.04s ===============================

Ještě lépe jsou výsledky patrné při pohledu na vygenerovanou HTML stránku:

Obrázek 4: Podrobnější výsledky s pokrytím zdrojového kódu jednotkovými testy. Vidíme, že nepokrytý zůstal pouze jediný řádek v testovaném zdrojovém kódu.

12. Využití komentáře # pragma: no cover

S využitím komentáře „# pragma: no cover“ je možné označit ty řádky ve zdrojovém kódu, které budou při výpočtu pokrytí zdrojových kódů ignorovány. Lze označit jednotlivé řádky, bloky (to již naznačuje lenost testera :-), funkce či dokonce celé třídy. Pochopitelně se jedná o dvousečnou a často zneužívanou vlastnost:

"""Výpočet seznamu prvočísel až do zadaného limitu."""
 
# originální kód lze nalézt na adrese:
# http://www.rosettacode.org/wiki/Sieve_of_Eratosthenes#Odds-only_version_of_the_array_sieve_above
 
 
def primes2(limit):
    """Výpočet seznamu prvočísel až do zadaného limitu."""
    # okrajový případ
    if limit < 2: # pragma: no cover
        return []
 
    # druhý případ - 2 je speciálním prvočíslem
    if limit < 3: # pragma: no cover
        return [2]
 
    lmtbf = (limit - 3) // 2
 
    # naplnění tabulky, která se bude prosívat
    buf = [True] * (lmtbf + 1)
 
    # vlastní prosívání
    for i in range((int(limit ** 0.5) - 3) // 2 + 1):
        if buf[i]:
            p = i + i + 3
            s = p * (i + 1) + i
            buf[s::p] = [False] * ((lmtbf - s) // p + 1)
 
    # vytvoření seznamu prvočísel
    return [2] + [i + i + 3 for i, v in enumerate(buf) if v]

Nové výsledky, nyní s (virtuálně) stoprocentním pokrytím:

============================= test session starts ==============================
platform linux -- Python 3.6.6, pytest-5.4.2, py-1.5.2, pluggy-0.13.1
rootdir: /home/ptisnovs/src/python/testing-in-python/pytest/primes3
plugins: voluptuous-1.0.2, cov-2.5.1
collected 2 items
 
test_primes.py ..                                                        [100%]
 
----------- coverage: platform linux, python 3.6.6-final-0 -----------
Name        Stmts   Miss  Cover   Missing
-----------------------------------------
primes.py       9      0   100%
 
 
============================== 2 passed in 0.04s ===============================

Obrázek 5: Pohled na výsledky exportované do HTML prozradí, jak je to se stoprocentním pokrytím ve skutečnosti.

13. Skutečné plné pokrytí kódu testy

Samozřejmě je – alespoň v našem jednoduchém projektu – možné i bez výše uvedeného triku dosáhnout stoprocentního pokrytí jednotkovými testy. Ani není nutné používat mocky či další triky; pouze postačuje jednotkové testy rozšířit a zjistit, jak se testovaná funkce chová pro vstupní hodnoty 0, 1 a 2:

"""Implementace jednotkových testů."""
 
from primes import primes2
 
 
def test_primes_10():
    """Otestování výpočtu seznamu prvočísel až do limitu 10."""
    # získat seznam prvočísel až do limitu 10
    p = primes2(10)
    # testy lze dále rozšiřovat
    assert 2 in p
    assert 10 not in p
 
 
def test_primes_0():
    """Otestování výpočtu seznamu prvočísel do limitu 0."""
    p = primes2(0)
    # otestujeme, zda je sekvence prázdná (není zcela přesné)
    assert not p
 
 
def test_primes_2():
    """Otestování výpočtu seznamu prvočísel do limitu 2."""
    p = primes2(2)
    # otestujeme, zda sekvence obsahuje pouze hodnotu 2
    assert 2 in p
    assert p == [2]
Poznámka: obecně platí, že čisté funkce (bez vedlejších efektů, ideálně bez přístupu ke globálním nebo nelokálním hodnotám) lze testovat velmi snadno, takže čím lépe je projekt s tímto ohledem navržen, tím snadnější je i jeho testování.

Výsledky nyní plně odpovídají naší snaze:

============================= test session starts ==============================
platform linux -- Python 3.6.6, pytest-5.4.2, py-1.5.2, pluggy-0.13.1
rootdir: /home/ptisnovs/src/python/testing-in-python/pytest/primes4
plugins: voluptuous-1.0.2, cov-2.5.1
collected 3 items
 
test_primes.py ...                                                       [100%]
 
----------- coverage: platform linux, python 3.6.6-final-0 -----------
Name        Stmts   Miss  Cover   Missing
-----------------------------------------
primes.py      13      0   100%
 
 
============================== 3 passed in 0.04s ===============================

Obrázek 6: Stoprocentní pokrytí zdrojového kódu jednotkovými testy.

14. Benchmarky spouštěné v rámci jednotkových testů

V rámci testovacího frameworku pytest je možné spouštět i benchmarky s případným vyhodnocením jejich výsledků, porovnáním několika benchmarků, vygenerováním grafu s výsledky atd. Aby byly benchmarky použitelné, je nutné nainstalovat nový plugin nazvaný pytest-benchmark, což se provede například takto (opět pouze pro aktuálně přihlášeného uživatele):

$ pip3 install --user pytest-benchmark

Průběh instalace:

    ...
    ...
    ...
    100% |████████████████████████████████| 51kB 806kB/s
Collecting pytest>=3.8 (from pytest-benchmark)
  Downloading https://files.pythonhosted.org/packages/6c/f9/9f2b6c672c8f8bb87a4c1bd52c1b57213627b035305aad745d015b2a62ae/pytest-5.4.2-py3-none-any.whl (247kB)
    100% |████████████████████████████████| 256kB 1.5MB/s
Collecting py-cpuinfo (from pytest-benchmark)
  Downloading https://files.pythonhosted.org/packages/42/60/63f28a5401da733043abe7053e7d9591491b4784c4f87c339bf51215aa0a/py-cpuinfo-5.0.0.tar.gz (82kB)
    100% |████████████████████████████████| 92kB 3.1MB/s
Collecting more-itertools>=4.0.0 (from pytest>=3.8->pytest-benchmark)
  Downloading https://files.pythonhosted.org/packages/72/96/4297306cc270eef1e3461da034a3bebe7c84eff052326b130824e98fc3fb/more_itertools-8.2.0-py3-none-any.whl (43kB)
    100% |████████████████████████████████| 51kB 2.2MB/s
Requirement already satisfied: py>=1.5.0 in /usr/lib/python3.6/site-packages (from pytest>=3.8->pytest-benchmark)
Collecting importlib-metadata>=0.12; python_version < "3.8" (from pytest>=3.8->pytest-benchmark)
  Downloading https://files.pythonhosted.org/packages/ad/e4/891bfcaf868ccabc619942f27940c77a8a4b45fd8367098955bb7e152fb1/importlib_metadata-1.6.0-py2.py3-none-any.whl
Collecting pluggy<1.0,>=0.12 (from pytest>=3.8->pytest-benchmark)
  Downloading https://files.pythonhosted.org/packages/a0/28/85c7aa31b80d150b772fbe4a229487bc6644da9ccb7e427dd8cc60cb8a62/pluggy-0.13.1-py2.py3-none-any.whl
Requirement already satisfied: attrs>=17.4.0 in /usr/lib/python3.6/site-packages (from pytest>=3.8->pytest-benchmark)
Requirement already satisfied: wcwidth in ./.local/lib/python3.6/site-packages (from pytest>=3.8->pytest-benchmark)
Collecting packaging (from pytest>=3.8->pytest-benchmark)
  Downloading https://files.pythonhosted.org/packages/62/0a/34641d2bf5c917c96db0ded85ae4da25b6cd922d6b794648d4e7e07c88e5/packaging-20.3-py2.py3-none-any.whl
Collecting zipp>=0.5 (from importlib-metadata>=0.12; python_version < "3.8"->pytest>=3.8->pytest-benchmark)
  Downloading https://files.pythonhosted.org/packages/b2/34/bfcb43cc0ba81f527bc4f40ef41ba2ff4080e047acb0586b56b3d017ace4/zipp-3.1.0-py3-none-any.whl
Requirement already satisfied: pyparsing>=2.0.2 in /usr/lib/python3.6/site-packages (from packaging->pytest>=3.8->pytest-benchmark)
Requirement already satisfied: six in ./.local/lib/python3.6/site-packages (from packaging->pytest>=3.8->pytest-benchmark)
Installing collected packages: more-itertools, zipp, importlib-metadata, pluggy, packaging, pytest, py-cpuinfo, pytest-benchmark
  Found existing installation: pytest 2.9.2
    Uninstalling pytest-2.9.2:
      Successfully uninstalled pytest-2.9.2
  Running setup.py install for py-cpuinfo ... done
Successfully installed importlib-metadata-1.6.0 more-itertools-8.2.0 packaging-20.3 pluggy-0.13.1 py-cpuinfo-5.0.0 pytest-5.4.2 pytest-benchmark-3.2.3 zipp-3.1.0

Pokud nyní spustíte již existující jednotkové testy, bude jedinou viditelnou změnou nový řádek informující o pluginu benchmark a pochopitelně i jméno (a verze) tohoto pluginu vypsaná v rámci sekce „plugins“. Způsob spouštění testů i jejich vyhodnocení prozatím nebudou žádným způsobem pozměněny:

============================= test session starts ==============================
platform linux -- Python 3.6.6, pytest-5.4.2, py-1.5.2, pluggy-0.13.1
benchmark: 3.2.3 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000)
rootdir: /home/ptisnovs/src/python/testing-in-python/pytest/primes4
plugins: voluptuous-1.0.2, benchmark-3.2.3, cov-2.5.1
collected 3 items
 
test_primes.py ...                                                       [100%]
 
----------- coverage: platform linux, python 3.6.6-final-0 -----------
Name        Stmts   Miss  Cover   Missing
-----------------------------------------
primes.py      13      0   100%
 
 
============================== 3 passed in 0.04s ===============================
Poznámka: další změnou, kterou ovšem zaregistrujete až po spuštění testů, je existence nového podadresáře, jenž obsahuje uložené výsledky benchmarků. Tento podadresář je pouze dočasný a neměl by být oficiální součástí repositáře se zdrojovými kódy.

15. Jednoduchý benchmark

„fast functions are no good if they return incorrect results“

Napsání benchmarku je ve skutečnosti snadné. Ukážeme si to na příkladu následujícího testu, který spočítá prvočísla v rozsahu 0 až 10:

def test_primes_10():
    """Otestování výpočtu seznamu prvočísel až do limitu 10."""
    # získat seznam prvočísel až do limitu 10
    p = primes2(10)
    # testy lze dále rozšiřovat
    assert 2 in p
    assert 10 not in p

Převod jednotkového testu na benchmark se provede jen s využitím dvou změn:

  1. Přidáním parametru benchmark do funkce představující jednotkový test
  2. Změnou volání primes2(10) za benchmark(primes2, 10)

Výsledek může vypadat takto:

def test_primes_10(benchmark):
    """Otestování výpočtu seznamu prvočísel až do limitu 10."""
    # získat seznam prvočísel až do limitu 10
    p = benchmark(primes2, 10)
    # testy lze dále rozšiřovat
    assert 2 in p
    assert 10 not in p

Podobně lze přepsat i další testy:

"""Implementace jednotkových testů."""
 
from primes import primes2
 
 
def test_primes_10(benchmark):
    """Otestování výpočtu seznamu prvočísel až do limitu 10."""
    # získat seznam prvočísel až do limitu 10
    p = benchmark(primes2, 10)
    # testy lze dále rozšiřovat
    assert 2 in p
    assert 10 not in p
 
 
def test_primes_100000(benchmark):
    """Otestování výpočtu seznamu prvočísel až do limitu 100000."""
    # získat seznam prvočísel až do limitu 100000
    p = benchmark(primes2, 100000)
    # testy lze dále rozšiřovat
    assert 2 in p
    assert 10 not in p
    # hodnoty získány ze seznamu:
    # https://primes.utm.edu/lists/small/10000.txt
    assert 99989 in p
    assert 99991 in p
 
 
def test_primes_0(benchmark):
    """Otestování výpočtu seznamu prvočísel do limitu 0."""
    p = benchmark(primes2, 0)
    # otestujeme, zda je sekvence prázdná (není zcela přesné)
    assert not p
 
 
def test_primes_2(benchmark):
    """Otestování výpočtu seznamu prvočísel do limitu 2."""
    p = benchmark(primes2, 2)
    # otestujeme, zda sekvence obsahuje pouze hodnotu 2
    assert 2 in p
    assert p == [2]
Poznámka: povšimněte si, že jsme přidali i benchmark pro nalezení prvočísel v rozsahu 0 až 100000, což v Pythonu (čistý interpret v případě CPythonu) není zcela triviální operace.

Nyní bude spuštění testů vypadat odlišně, protože se ukážou i výsledky benchmarků:

============================= test session starts ==============================
platform linux -- Python 3.6.6, pytest-5.4.2, py-1.5.2, pluggy-0.13.1
benchmark: 3.2.3 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000)
rootdir: /home/ptisnovs/src/python/testing-in-python/pytest/primes5
plugins: voluptuous-1.0.2, benchmark-3.2.3, cov-2.5.1
collected 4 items
 
test_primes.py ....                                                      [100%]
 
-------------------------------------------------------------------------------------------------------- benchmark: 4 tests -------------------------------------------------------------------------------------------------------
Name (time in ns)                 Min                       Max                      Mean                 StdDev                    Median                    IQR            Outliers             OPS            Rounds  Iterations
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
test_primes_0                151.4600 (1.0)            524.9801 (1.0)            154.5897 (1.0)          13.1643 (1.0)            153.5499 (1.0)           0.8599 (1.0)      977;1633  6,468,736.2669 (1.0)       65215         100
test_primes_2                207.6100 (1.37)           797.1500 (1.52)           214.1976 (1.39)         19.9082 (1.51)           212.8500 (1.39)          1.5101 (1.76)     329;2333  4,668,585.6110 (0.72)      47779         100
test_primes_10             2,988.0030 (19.73)       27,753.9948 (52.87)        3,215.3491 (20.80)       723.2469 (54.94)        3,127.0029 (20.36)        54.9917 (63.95)   2480;4210    311,008.2159 (0.05)      96414           1
test_primes_100000     3,287,255.0073 (>1000.0)  3,790,438.0006 (>1000.0)  3,335,102.2510 (>1000.0)  62,347.3199 (>1000.0)  3,309,603.0093 (>1000.0)  37,557.2527 (>1000.0)     27;27        299.8409 (0.00)        291           1
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 
Legend:
  Outliers: 1 Standard Deviation from Mean; 1.5 IQR (InterQuartile Range) from 1st Quartile and 3rd Quartile.
  OPS: Operations Per Second, computed as 1 / Mean
============================== 4 passed in 5.62s ===============================
Poznámka: čísla v závorce udávají, kolikrát je daný test pomalejší, než nejrychlejší benchmark.

Z výsledků je patrné, že se každý test opakuje několikrát (výchozí hodnotou je jedna sekunda), takže lze spočítat nejkratší a nejrychlejší běh, průměr, odchylku, počet provedených operací aj. Pokud nám nevyhovuje, že je každý test spuštěn pouze tolikrát, dokud není tohoto (či jiného) času dosaženo (poslední dva sloupce), lze specifikovat počet opakování:

$ pytest --benchmark-min-rounds=100000

Taktéž je možné nastavit zavolání testovaného kódu „pro zahřátí“, tj. bez sledování dosaženého času. Tím lze vyloučit klasický problém – pozdní inicializaci, naplnění cache pamětí, možné JIT optimalizace (nikoli v CPythonu) atd.:

$ pytest --benchmark-min-rounds=100000 --benchmark-warmup-iterations=10000

16. Dvě verze algoritmu s jejich porovnáním

V této kapitole si ukážeme dvě možné verze algoritmu pro výpočet prvočísel s využitím Eratosthenova síta. První výpočet je přímočarý (a dalo by se říci, že naivní):

# originální kód lze nalézt na adrese:
# http://www.rosettacode.org/wiki/Sieve_of_Eratosthenes#Using_array_lookup
def primes2(limit):
    is_prime = [False] * 2 + [True] * (limit - 1)
    for n in range(int(limit**0.5 + 1.5)): # stop at ``sqrt(limit)``
        if is_prime[n]:
            for i in range(n*n, limit+1, n):
                is_prime[i] = False
    return [i for i, prime in enumerate(is_prime) if prime]
# originální kód lze nalézt na adrese:
# http://www.rosettacode.org/wiki/Sieve_of_Eratosthenes#Odds-only_version_of_the_array_sieve_above
def primes2(limit):
    """Výpočet seznamu prvočísel až do zadaného limitu."""
    # okrajový případ
    if limit < 2:
        return []

    # druhý případ - 2 je speciálním prvočíslem
    if limit < 3:
        return [2]

    lmtbf = (limit - 3) // 2

    # naplnění tabulky, která se bude prosívat
    buf = [True] * (lmtbf + 1)

    # vlastní prosívání
    for i in range((int(limit ** 0.5) - 3) // 2 + 1):
        if buf[i]:
            p = i + i + 3
            s = p * (i + 1) + i
            buf[s::p] = [False] * ((lmtbf - s) // p + 1)

    # vytvoření seznamu prvočísel
    return [2] + [i + i + 3 for i, v in enumerate(buf) if v]

17. Porovnání výsledků benchmarku

Po úpravě zdrojového kódu je možné benchmark spustit znovu a současně si nechat porovnat nové výsledky s výsledky staršími. Použijeme tedy následující příkaz:

$ pytest --benchmark-compare
Poznámka: na příkazové řádce lze v případě potřeby specifikovat, které výsledky se mají porovnat. Bez této specifikace se porovná předchozí výsledek s výsledkem současným (NOW).

Výsledky porovnání vyžadují pohled na test označený příznakem NOW a porovnat ho se stejně pojmenovaným testem, tentokrát (v mém případě) s příznakem 0001_b21f3d5:

test_primes_100000 (NOW)               3,333,477.0051 (>1000.0)   3,857,687.9961 (>1000.0)   3,469,644.2332 (>1000.0)  140,171.5543 (>1000.0)   3,416,847.9979 (>1000.0)  215,252.2557 (>1000.0)      41;0        288.2140 (0.00)        253           1
test_primes_100000 (0001_b21f3d5)     13,335,698.9973 (>1000.0)  17,059,773.0030 (>1000.0)  14,246,669.9671 (>1000.0)  774,731.3323 (>1000.0)  14,071,077.4976 (>1000.0)  776,759.5016 (>1000.0)       9;3         70.1918 (0.00)         60           1

Z těchto výsledků je patrné, že jak průměrný, tak i nejlepší i nejhorší výsledky druhého (optimalizovaného) algoritmu jsou mnohem lepší, než algoritmu řekněme naivního (s přímočarou implementací Eratosthenova síta).

Výsledky porovnání všech testů (benchmarků) vypadají následovně:

hacking_tip

============================= test session starts ==============================
platform linux -- Python 3.6.6, pytest-5.4.2, py-1.5.2, pluggy-0.13.1
benchmark: 3.2.3 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000)
rootdir: /home/ptisnovs/src/python/testing-in-python/pytest/primes6
plugins: voluptuous-1.0.2, benchmark-3.2.3, cov-2.5.1
collected 4 items
 
test_primes.py ....                                                      [100%]
--------------------------------------------------------------------------------------------------------------- benchmark: 8 tests ------------------------------------------------------------------------------------------------------------------
Name (time in ns)                                 Min                        Max                       Mean                  StdDev                     Median                     IQR            Outliers             OPS            Rounds  Iterations
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
test_primes_0 (NOW)                          150.2500 (1.0)           4,759.9200 (3.09)            156.8916 (1.0)           33.5549 (1.0)             152.5300 (1.0)            0.9000 (1.0)     1203;8878  6,373,826.0269 (1.0)       65181         100
test_primes_2 (NOW)                          214.3912 (1.43)          1,542.6087 (1.0)             226.6960 (1.44)          66.0965 (1.97)            219.1740 (1.44)           1.9133 (2.13)   2153;30710  4,411,194.4164 (0.69)     197785          23
test_primes_0 (0001_b21f3d5)               1,925.9969 (12.82)        29,465.9985 (19.10)         2,182.2444 (13.91)      1,118.5913 (33.34)         2,077.9989 (13.62)         58.0039 (64.45)  2728;11359    458,243.8158 (0.07)     177652           1
test_primes_2 (0001_b21f3d5)               2,130.0002 (14.18)        32,203.9996 (20.88)         2,367.3029 (15.09)      1,021.6890 (30.45)         2,283.9959 (14.97)         55.9958 (62.22)   2220;9148    422,421.6525 (0.07)     164420           1
test_primes_10 (NOW)                       2,973.9967 (19.79)        30,780.9969 (19.95)         3,230.8981 (20.59)      1,169.5545 (34.85)         3,134.0023 (20.55)         64.0066 (71.12)    628;4001    309,511.4637 (0.05)      68265           1
test_primes_10 (0001_b21f3d5)              3,479.0064 (23.15)        30,729.0029 (19.92)         3,975.2943 (25.34)      1,327.4789 (39.56)         3,756.9989 (24.63)        359.9998 (400.02)   716;2195    251,553.7048 (0.04)      49148           1
test_primes_100000 (NOW)               3,333,477.0051 (>1000.0)   3,857,687.9961 (>1000.0)   3,469,644.2332 (>1000.0)  140,171.5543 (>1000.0)   3,416,847.9979 (>1000.0)  215,252.2557 (>1000.0)      41;0        288.2140 (0.00)        253           1
test_primes_100000 (0001_b21f3d5)     13,335,698.9973 (>1000.0)  17,059,773.0030 (>1000.0)  14,246,669.9671 (>1000.0)  774,731.3323 (>1000.0)  14,071,077.4976 (>1000.0)  776,759.5016 (>1000.0)       9;3         70.1918 (0.00)         60           1
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 
Legend:
  Outliers: 1 Standard Deviation from Mean; 1.5 IQR (InterQuartile Range) from 1st Quartile and 3rd Quartile.
  OPS: Operations Per Second, computed as 1 / Mean
============================== 4 passed in 6.25s ===============================

18. Repositář s demonstračními příklady

Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do nového Git repositáře, který je dostupný na adrese https://github.com/tisnik/testing-in-python. V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, dnes má přibližně deseti kilobajtů), můžete namísto toho použít odkazy na jednotlivé demonstrační příklady a jejich části, které naleznete v následující tabulce:

# Příklad Stručný popis Cesta
1 main.py vstupní bod do testované aplikace https://github.com/tisnik/testing-in-python/blob/master/unittest_mock/mock-testB/main.py
2 module1.py první modul https://github.com/tisnik/testing-in-python/blob/master/unittest_mock/mock-testB/module1.py
3 module2.py druhý modul https://github.com/tisnik/testing-in-python/blob/master/unittest_mock/mock-testB/module2.py
4 module3.py třetí modul https://github.com/tisnik/testing-in-python/blob/master/unittest_mock/mock-testB/module3.py
5 test.py implementace jednotkových testů využívajících několik dekorátorů @patch https://github.com/tisnik/testing-in-python/blob/master/unittest_mock/mock-testB/test.py
6 run skript pro spuštění aplikace https://github.com/tisnik/testing-in-python/blob/master/unittest_mock/mock-testB/run
7 test skript pro spuštění jednotkových testů https://github.com/tisnik/testing-in-python/blob/master/unittest_mock/mock-testB/test
       
8 main.py vstupní bod do testované aplikace https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes1/main.py
9 primes.py výpočet prvočísel s využitím Eratosthenova síta https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes1/primes.py
10 test_primes.py jednotkové testy https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes1/test_primes.py
11 run skript pro spuštění aplikace https://github.com/tisnik/testing-in-python/blob/master/pytest/primes1/run
12 test skript pro spuštění jednotkových testů https://github.com/tisnik/testing-in-python/blob/master/pytest/primes1/test
13 pytest.txt výsledek běhu jednotkových testů https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes1/pytest.txt
14 cov_all.txt pokrytí všech skriptů jednotkovými testy https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes1/cov_all.txt
15 cov_primes.txt pokrytí výpočtu jednotkovými testy https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes1/cov_primes.txt
16 cov_missing.txt podrobnější informace o řádcích nepokrytých testy https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes1/cov_missing.txt
       
17 main.py vstupní bod do testované aplikace https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes2/main.py
18 primes.py výpočet prvočísel s využitím Eratosthenova síta https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes2/primes.py
19 test_primes.py jednotkové testy https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes2/test_primes.py
20 run skript pro spuštění aplikace https://github.com/tisnik/testing-in-python/blob/master/pytest/primes2/run
21 test skript pro spuštění jednotkových testů https://github.com/tisnik/testing-in-python/blob/master/pytest/primes2/test
30 pytest.txt výsledek běhu jednotkových testů https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes2/pytest.txt
23 cov_all.txt pokrytí všech skriptů jednotkovými testy https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes2/cov_all.txt
24 cov_primes.txt pokrytí výpočtu jednotkovými testy https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes2/cov_primes.txt
25 cov_missing.txt podrobnější informace o řádcích nepokrytých testy https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes2/cov_missing.txt
       
26 main.py vstupní bod do testované aplikace https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes3/main.py
27 primes.py výpočet prvočísel s využitím Eratosthenova síta https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes3/primes.py
28 test_primes.py jednotkové testy https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes3/test_primes.py
29 run skript pro spuštění aplikace https://github.com/tisnik/testing-in-python/blob/master/pytest/primes3/run
30 test skript pro spuštění jednotkových testů https://github.com/tisnik/testing-in-python/blob/master/pytest/primes3/test
39 pytest.txt výsledek běhu jednotkových testů https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes3/pytest.txt
32 cov_all.txt pokrytí všech skriptů jednotkovými testy https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes3/cov_all.txt
33 cov_primes.txt pokrytí výpočtu jednotkovými testy https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes3/cov_primes.txt
34 cov_missing.txt podrobnější informace o řádcích nepokrytých testy https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes3/cov_missing.txt
       
35 main.py vstupní bod do testované aplikace https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes4/main.py
36 primes.py výpočet prvočísel s využitím Eratosthenova síta https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes4/primes.py
37 test_primes.py jednotkové testy https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes4/test_primes.py
38 run skript pro spuštění aplikace https://github.com/tisnik/testing-in-python/blob/master/pytest/primes4/run
39 test skript pro spuštění jednotkových testů https://github.com/tisnik/testing-in-python/blob/master/pytest/primes4/test
48 pytest.txt výsledek běhu jednotkových testů https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes4/pytest.txt
41 cov_all.txt pokrytí všech skriptů jednotkovými testy https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes4/cov_all.txt
42 cov_primes.txt pokrytí výpočtu jednotkovými testy https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes4/cov_primes.txt
43 cov_missing.txt podrobnější informace o řádcích nepokrytých testy https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes4/cov_missing.txt
       
44 main.py vstupní bod do testované aplikace https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes5/main.py
45 primes.py výpočet prvočísel s využitím Eratosthenova síta https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes5/primes.py
46 test_primes.py jednotkové testy https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes5/test_primes.py
47 run skript pro spuštění aplikace https://github.com/tisnik/testing-in-python/blob/master/pytest/primes5/run
48 benchmark skript pro spuštění benchmarku https://github.com/tisnik/testing-in-python/blob/master/pytest/primes5/test
57 benchmark.txt výsledek běhu benchmarku https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes5/pytest.txt
       
58 main.py vstupní bod do testované aplikace https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes6/main.py
59 primes.py výpočet prvočísel s využitím Eratosthenova síta https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes6/primes.py
60 test_primes.py jednotkové testy https://github.com/tisnik/testing-in-python/blob/master/pytest/pri­mes6/test_primes.py
61 run skript pro spuštění aplikace https://github.com/tisnik/testing-in-python/blob/master/pytest/primes6/run
62 benchmark skript pro spuštění benchmarku https://github.com/tisnik/testing-in-python/blob/master/pytest/primes6/test

19. Předchozí články s tématem testování (nejenom) v Pythonu

Tématem testování jsme se již na stránkách Rootu několikrát zabývali. Jedná se mj. o následující články:

  1. Behavior-driven development v Pythonu s využitím knihovny Behave
    https://www.root.cz/clanky/behavior-driven-development-v-pythonu-s-vyuzitim-knihovny-behave/
  2. Behavior-driven development v Pythonu s využitím knihovny Behave (druhá část)
    https://www.root.cz/clanky/behavior-driven-development-v-pythonu-s-vyuzitim-knihovny-behave-druha-cast/
  3. Behavior-driven development v Pythonu s využitím knihovny Behave (závěrečná část)
    https://www.root.cz/clanky/behavior-driven-development-v-pythonu-s-vyuzitim-knihovny-behave-zaverecna-cast/
  4. Validace datových struktur v Pythonu pomocí knihoven Schemagic a Schema
    https://www.root.cz/clanky/validace-datovych-struktur-v-pythonu-pomoci-knihoven-schemagic-a-schema/
  5. Validace datových struktur v Pythonu (2. část)
    https://www.root.cz/clanky/validace-datovych-struktur-v-pythonu-2-cast/
  6. Validace datových struktur v Pythonu (dokončení)
    https://www.root.cz/clanky/validace-datovych-struktur-v-pythonu-dokonceni/
  7. Univerzální testovací nástroj Robot Framework
    https://www.root.cz/clanky/univerzalni-testovaci-nastroj-robot-framework/
  8. Univerzální testovací nástroj Robot Framework a BDD testy
    https://www.root.cz/clanky/univerzalni-testovaci-nastroj-robot-framework-a-bdd-testy/
  9. Úvod do problematiky fuzzingu a fuzz testování
    https://www.root.cz/clanky/uvod-do-problematiky-fuzzingu-a-fuzz-testovani/
  10. Úvod do problematiky fuzzingu a fuzz testování – složení vlastního fuzzeru
    https://www.root.cz/clanky/uvod-do-problematiky-fuzzingu-a-fuzz-testovani-slozeni-vlastniho-fuzzeru/
  11. Knihovny a moduly usnadňující testování aplikací naprogramovaných v jazyce Clojure
    https://www.root.cz/clanky/knihovny-a-moduly-usnadnujici-testovani-aplikaci-naprogramovanych-v-jazyce-clojure/
  12. Validace dat s využitím knihovny spec v Clojure 1.9.0
    https://www.root.cz/clanky/validace-dat-s-vyuzitim-knihovny-spec-v-clojure-1–9–0/
  13. Testování aplikací naprogramovaných v jazyce Go
    https://www.root.cz/clanky/testovani-aplikaci-naprogramovanych-v-jazyce-go/
  14. Knihovny určené pro tvorbu testů v programovacím jazyce Go
    https://www.root.cz/clanky/knihovny-urcene-pro-tvorbu-testu-v-programovacim-jazyce-go/
  15. Testování aplikací psaných v Go s využitím knihoven Goblin a Frisby
    https://www.root.cz/clanky/testovani-aplikaci-psanych-v-go-s-vyuzitim-knihoven-goblin-a-frisby/
  16. Testování Go aplikací s využitím knihovny GΩmega a frameworku Ginkgo
    https://www.root.cz/clanky/testovani-go-aplikaci-s-vyuzitim-knihovny-gomega-mega-a-frameworku-ginkgo/
  17. Tvorba BDD testů s využitím jazyka Go a nástroje godog
    https://www.root.cz/clanky/tvorba-bdd-testu-s-vyuzitim-jazyka-go-a-nastroje-godog/
  18. Použití Go pro automatizaci práce s aplikacemi s interaktivním příkazovým řádkem
    https://www.root.cz/clanky/pouziti-go-pro-automatizaci-prace-s-aplikacemi-s-interaktivnim-prikazovym-radkem/
  19. Použití Go pro automatizaci práce s aplikacemi s interaktivním příkazovým řádkem (dokončení)
    https://www.root.cz/clanky/pouziti-go-pro-automatizaci-prace-s-aplikacemi-s-interaktivnim-prikazovym-radkem-dokonceni/
  20. Použití jazyka Gherkin při tvorbě testovacích scénářů pro aplikace psané v Clojure
    https://www.root.cz/clanky/pouziti-jazyka-gherkin-pri-tvorbe-testovacich-scenaru-pro-aplikace-psane-v-nbsp-clojure/
  21. Použití jazyka Gherkin při tvorbě testovacích scénářů pro aplikace psané v Clojure (2)
    https://www.root.cz/clanky/pouziti-jazyka-gherkin-pri-tvorbe-testovacich-scenaru-pro-aplikace-psane-v-nbsp-clojure-2/
  22. pytest 5.4.2 na PyPi
    https://pypi.org/project/pytest/
  23. Awesome Python – testing
    https://github.com/vinta/awesome-python#testing
  24. pytest Plugins Compatibility
    http://plugincompat.herokuapp.com/
  25. Selenium (pro Python)
    https://pypi.org/project/selenium/
  26. Getting Started With Testing in Python
    https://realpython.com/python-testing/
  27. unittest.mock — mock object library
    https://docs.python.org/3­.5/library/unittest.mock.html
  28. mock 2.0.0
    https://pypi.python.org/pypi/mock
  29. An Introduction to Mocking in Python
    https://www.toptal.com/python/an-introduction-to-mocking-in-python
  30. Mock – Mocking and Testing Library
    http://mock.readthedocs.io/en/stable/
  31. Python Mocking 101: Fake It Before You Make It
    https://blog.fugue.co/2016–02–11-python-mocking-101.html
  32. Nauč se Python! – Testování
    http://naucse.python.cz/les­sons/intro/testing/
  33. Flexmock (dokumentace)
    https://flexmock.readthedoc­s.io/en/latest/
  34. Test Fixture (Wikipedia)
    https://en.wikipedia.org/wi­ki/Test_fixture
  35. Mock object (Wikipedia)
    https://en.wikipedia.org/wi­ki/Mock_object
  36. Extrémní programování
    https://cs.wikipedia.org/wi­ki/Extr%C3%A9mn%C3%AD_pro­gramov%C3%A1n%C3%AD
  37. Programování řízené testy
    https://cs.wikipedia.org/wi­ki/Programov%C3%A1n%C3%AD_%C5%99%­C3%ADzen%C3%A9_testy
  38. Pip (dokumentace)
    https://pip.pypa.io/en/stable/
  39. Tox
    https://tox.readthedocs.io/en/latest/
  40. pytest: helps you write better programs
    https://docs.pytest.org/en/latest/
  41. doctest — Test interactive Python examples
    https://docs.python.org/dev/li­brary/doctest.html#module-doctest
  42. unittest — Unit testing framework
    https://docs.python.org/dev/li­brary/unittest.html
  43. Python namespaces
    https://bytebaker.com/2008/07/30/pyt­hon-namespaces/
  44. Namespaces and Scopes
    https://www.python-course.eu/namespaces.php
  45. Stránka projektu Robot Framework
    https://robotframework.org/
  46. GitHub repositář Robot Frameworku
    https://github.com/robotfra­mework/robotframework
  47. Robot Framework (Wikipedia)
    https://en.wikipedia.org/wi­ki/Robot_Framework
  48. Tutoriál Robot Frameworku
    http://www.robotframeworktu­torial.com/
  49. Robot Framework Documentation
    https://robotframework.or­g/robotframework/
  50. Robot Framework Introduction
    https://blog.testproject.i­o/2016/11/22/robot-framework-introduction/
  51. robotframework 3.1.2 na PyPi
    https://pypi.org/project/ro­botframework/
  52. Robot Framework demo (GitHub)
    https://github.com/robotfra­mework/RobotDemo
  53. Robot Framework web testing demo using SeleniumLibrary
    https://github.com/robotfra­mework/WebDemo
  54. Robot Framework for Mobile Test Automation Demo
    https://www.youtube.com/wat­ch?v=06LsU08slP8
  55. Gherkin
    https://cucumber.io/docs/gherkin/
  56. Selenium
    https://selenium.dev/
  57. SeleniumLibrary
    https://robotframework.org/
  58. The Practical Test Pyramid
    https://martinfowler.com/ar­ticles/practical-test-pyramid.html
  59. Acceptance Tests and the Testing Pyramid
    http://www.blog.acceptance­testdrivendevelopment.com/ac­ceptance-tests-and-the-testing-pyramid/
  60. Tab-separated values
    https://en.wikipedia.org/wiki/Tab-separated_values
  61. A quick guide about Python implementations
    https://blog.rmotr.com/a-quick-guide-about-python-implementations-aa224109f321
  62. radamsa
    https://gitlab.com/akihe/radamsa
  63. Fuzzing (Wikipedia)
    https://en.wikipedia.org/wiki/Fuzzing
  64. american fuzzy lop
    http://lcamtuf.coredump.cx/afl/
  65. Fuzzing: the new unit testing
    https://go-talks.appspot.com/github.com/dvyukov/go-fuzz/slides/fuzzing.slide#1
  66. Corpus for github.com/dvyukov/go-fuzz examples
    https://github.com/dvyukov/go-fuzz-corpus
  67. AFL – QuickStartGuide.txt
    https://github.com/google/AF­L/blob/master/docs/QuickStar­tGuide.txt
  68. Introduction to Fuzzing in Python with AFL
    https://alexgaynor.net/2015/a­pr/13/introduction-to-fuzzing-in-python-with-afl/
  69. Writing a Simple Fuzzer in Python
    https://jmcph4.github.io/2018/01/19/wri­ting-a-simple-fuzzer-in-python/
  70. How to Fuzz Go Code with go-fuzz (Continuously)
    https://fuzzit.dev/2019/10/02/how-to-fuzz-go-code-with-go-fuzz-continuously/
  71. Golang Fuzzing: A go-fuzz Tutorial and Example
    http://networkbit.ch/golang-fuzzing/
  72. Fuzzing Python Modules
    https://stackoverflow.com/qu­estions/20749026/fuzzing-python-modules
  73. 0×3 Python Tutorial: Fuzzer
    http://www.primalsecurity.net/0×3-python-tutorial-fuzzer/
  74. fuzzing na PyPi
    https://pypi.org/project/fuzzing/
  75. Fuzzing 0.3.2 documentation
    https://fuzzing.readthedoc­s.io/en/latest/
  76. Randomized testing for Go
    https://github.com/dvyukov/go-fuzz
  77. HTTP/2 fuzzer written in Golang
    https://github.com/c0nrad/http2fuzz
  78. Ffuf (Fuzz Faster U Fool) – An Open Source Fast Web Fuzzing Tool
    https://hacknews.co/hacking-tools/20191208/ffuf-fuzz-faster-u-fool-an-open-source-fast-web-fuzzing-tool.html
  79. Continuous Fuzzing Made Simple
    https://fuzzit.dev/
  80. Halt and Catch Fire
    https://en.wikipedia.org/wi­ki/Halt_and_Catch_Fire#In­tel_x86
  81. Random testing
    https://en.wikipedia.org/wi­ki/Random_testing
  82. Monkey testing
    https://en.wikipedia.org/wi­ki/Monkey_testing
  83. Fuzzing for Software Security Testing and Quality Assurance, Second Edition
    https://books.google.at/bo­oks?id=tKN5DwAAQBAJ&pg=PR15&lpg=PR15&q=%­22I+settled+on+the+term+fuz­z%22&redir_esc=y&hl=de#v=o­nepage&q=%22I%20settled%20on%20the%20ter­m%20fuzz%22&f=false
  84. libFuzzer – a library for coverage-guided fuzz testing
    https://llvm.org/docs/LibFuzzer.html
  85. fuzzy-swagger na PyPi
    https://pypi.org/project/fuzzy-swagger/
  86. fuzzy-swagger na GitHubu
    https://github.com/namuan/fuzzy-swagger
  87. Fuzz testing tools for Python
    https://wiki.python.org/mo­in/PythonTestingToolsTaxo­nomy#Fuzz_Testing_Tools
  88. A curated list of awesome Go frameworks, libraries and software
    https://github.com/avelino/awesome-go
  89. gofuzz: a library for populating go objects with random values
    https://github.com/google/gofuzz
  90. tavor: A generic fuzzing and delta-debugging framework
    https://github.com/zimmski/tavor
  91. hypothesis na GitHubu
    https://github.com/Hypothe­sisWorks/hypothesis
  92. Hypothesis: Test faster, fix more
    https://hypothesis.works/
  93. Hypothesis
    https://hypothesis.works/ar­ticles/intro/
  94. What is Hypothesis?
    https://hypothesis.works/articles/what-is-hypothesis/
  95. Databáze CVE
    https://www.cvedetails.com/
  96. Fuzz test Python modules with libFuzzer
    https://github.com/eerimoq/pyfuzzer
  97. Taof – The art of fuzzing
    https://sourceforge.net/pro­jects/taof/
  98. JQF + Zest: Coverage-guided semantic fuzzing for Java
    https://github.com/rohanpadhye/jqf
  99. http2fuzz
    https://github.com/c0nrad/http2fuzz
  100. Demystifying hypothesis testing with simple Python examples
    https://towardsdatascience­.com/demystifying-hypothesis-testing-with-simple-python-examples-4997ad3c5294
  101. Testování
    http://voho.eu/wiki/testovani/
  102. Unit testing (Wikipedia.en)
    https://en.wikipedia.org/wi­ki/Unit_testing
  103. Unit testing (Wikipedia.cz)
    https://cs.wikipedia.org/wi­ki/Unit_testing
  104. Unit Test vs Integration Test
    https://www.youtube.com/wat­ch?v=0GypdsJulKE
  105. TestDouble
    https://martinfowler.com/bli­ki/TestDouble.html
  106. Test Double
    http://xunitpatterns.com/Tes­t%20Double.html
  107. Test-driven development (Wikipedia)
    https://en.wikipedia.org/wiki/Test-driven_development
  108. Acceptance test–driven development
    https://en.wikipedia.org/wi­ki/Acceptance_test%E2%80%93dri­ven_development
  109. Gauge
    https://gauge.org/
  110. Gauge (software)
    https://en.wikipedia.org/wi­ki/Gauge_(software)
  111. PYPL PopularitY of Programming Language
    https://pypl.github.io/PYPL.html
  112. Testing is Good. Pyramids are Bad. Ice Cream Cones are the Worst
    https://medium.com/@fistsOf­Reason/testing-is-good-pyramids-are-bad-ice-cream-cones-are-the-worst-ad94b9b2f05f
  113. Články a zprávičky věnující se Pythonu
    https://www.root.cz/n/python/
  114. PythonTestingToolsTaxonomy
    https://wiki.python.org/mo­in/PythonTestingToolsTaxo­nomy
  115. Top 6 BEST Python Testing Frameworks [Updated 2020 List]
    https://www.softwaretestin­ghelp.com/python-testing-frameworks/

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.