pytest
pytest – otwartoźródłowy framework do testowania jednostkowego i funkcjonalnego w języku Python. Zaprojektowany z myślą o prostocie zapisu testów oraz rozbudowanej możliwości rozszerzania poprzez wtyczki. Dzięki intuicyjnemu podejściu i bogatemu zestawowi funkcji stał się jednym z najpopularniejszych narzędzi testowych w ekosystemie Pythona.
Historia
Projekt powstał w 2003 roku jako py.test, autorstwa Holgera Krekela. Pierwsza publiczna wersja zyskała uznanie dzięki minimalistycznemu podejściu do definiowania testów. Od 2007 roku rozwijany jest intensywnie przez społeczność open source, a od 2010 przyjęto nazwę pytest, wprowadzając liczne ulepszenia architektury i API. Projekt jest licencjonowany na zasadach MIT License.
Kluczowe cechy
- Prosta konwencja nazewnictwa – testy są wykrywane automatycznie po nazwach zaczynających się od
test_lub kończących się na_test. - Wbudowane asercje języka Python, które nie wymagają oddzielnych metod
assertEqualitp. - Wsparcie dla fixture’ów – pozwalają definiować wspólne przygotowanie i sprzątanie środowiska testowego.
- Parametryzacja testów umożliwiająca uruchamianie jednej funkcji testowej z różnymi danymi wejściowymi.
- Rozszerzalność poprzez wtyczki – np.
pytest-cov(pokrycie kodu),pytest-xdist(równoległe wykonywanie testów),pytest-mock(mockowanie). - Integracja z innymi frameworkami testowymi, m.in. unittest, nose.
- Bogate raportowanie – wyjścia w formacie tekstowym, HTML, JUnit XML i inne.
Podstawowe elementy
Testy
Testy w pytest definiuje się jako zwykłe funkcje lub metody klas, które nie przyjmują argumentów (poza fixture’ami). Przykład najprostszego testu:
def test_sum():
assert sum([1, 2, 3]) == 6
Fixture’y
Fixture to specjalna funkcja oznaczona dekoratorem @pytest.fixture, której zadaniem jest przygotowanie środowiska testowego i opcjonalne sprzątanie po teście. Fixture’y mogą być współdzielone pomiędzy wieloma testami:
@pytest.fixture
def sample_data():
return [1, 2, 3]
def test_average(sample_data):
assert sum(sample_data) / len(sample_data) == 2
Parametryzacja
Dzięki dekoratorowi @pytest.mark.parametrize można uruchamiać ten sam test z różnymi zestawami danych:
@pytest.mark.parametrize("a,b,expected", [
(1, 2, 3),
(5, 7, 12),
(0, 0, 0)
])
def test_addition(a, b, expected):
assert a + b == expected
Uruchamianie testów
Podstawowe polecenie uruchamiające wszystkie testy w projekcie to:
pytest
Można dodać różne opcje, np.:
-v– tryb szczegółowy (verbose).-k "nazwa"– uruchamianie testów pasujących do podanego wzorca.--maxfail=3– zatrzymanie po trzech niepowodzeniach.--html=report.html– generowanie raportu w formacie HTML.
Rozszerzenia i wtyczki
Ekosystem pytest obejmuje setki wtyczek dostępnych w PyPI. Najczęściej używane:
- pytest-cov – pomiar pokrycia kodu testami.
- pytest-xdist – równoległe wykonywanie testów na wielu procesorach.
- pytest-mock – integracja z biblioteką
mock. - pytest-django – wsparcie dla aplikacji Django.
- pytest-asyncio – testowanie kodu asynchronicznego (
async/await).
Zastosowania
Framework jest wykorzystywany zarówno w małych projektach open source, jak i w dużych systemach komercyjnych. Dzięki elastyczności i możliwości integracji z CI/CD (np. Jenkins, GitHub Actions, GitLab CI) pytest stał się standardem w praktykach DevOps i ciągłej integracji.
Porównanie z innymi frameworkami
| Cecha | pytest | unittest | nose |
|---|---|---|---|
| Składnia asercji | Wbudowane assert |
Metody assert* |
Wbudowane assert |
| Fixture’y | Zaawansowane, z zakresem (function, module, session) | SetUp/TearDown w klasach | SetUp/TearDown, ograniczone |
| Parametryzacja | Wbudowana (@pytest.mark.parametrize) |
Brak natywnej | Wtyczki |
| Wtyczki | Setki, aktywna społeczność | Ograniczone | Obecne, ale mniej aktywne |
Przykładowy projekt
Poniżej przedstawiono prostą strukturę katalogów w projekcie wykorzystującym pytest:
my_project/
│
├─ src/
│ └─ calculator.py
│
├─ tests/
│ ├─ __init__.py
│ └─ test_calculator.py
└─ pyproject.toml # opcjonalna konfiguracja pytest
W pliku test_calculator.py mogą znajdować się testy takie jak:
import pytest
from src.calculator import add, divide
def test_add():
assert add(2, 3) == 5
@pytest.mark.parametrize("numerator,denominator,expected", [
(10, 2, 5),
(9, 3, 3),
(5, 2, 2.5)
])
def test_divide(numerator, denominator, expected):
assert divide(numerator, denominator) == expected
Konfiguracja
Ustawienia domyślne można nadpisać w pliku pytest.ini, tox.ini lub pyproject.toml. Przykładowa sekcja w pytest.ini:
[pytest]
addopts = -v --cov=src --html=report.html
testpaths = tests
python_files = test_*.py
Wspólnota i dalszy rozwój
Projekt jest rozwijany w otwartym modelu współpracy na platformie GitHub. Społeczność organizuje regularne spotkania (tzw. pytest-dev meetups) oraz utrzymuje listę planowanych funkcji w Roadmap. Dokumentacja dostępna jest w formie Read the Docs i jest stale aktualizowana.
Bibliografia
- Oficjalna dokumentacja pytest – Documentation
- Historia projektu – wpisy na GitHub repozytorium
- Poradniki i case studies – sekcja Tutoriale w encyklopedii