Pytest w Pythonie - od pierwszego testu do CI

Naukowczyni obserwuje maszynę do testowania kodu, gdzie `pytest` zarządza parametrami, wtyczkami i wynikami.

Napisano przez

Juliusz Król

Opublikowano

21 wrz 2026

Spis treści

Gdy aplikacja rośnie, ręczne sprawdzanie każdej zmiany szybko staje się wąskim gardłem. W tym artykule pokazuję, jak wykorzystać framework pytest do automatyzacji testów w Pythonie, od pierwszego prostego testu po fixtures, parametryzację i uruchamianie kontroli w CI. Skupiam się na rozwiązaniach, które rzeczywiście skracają pracę i pomagają wcześniej wykrywać błędy.

Automatyczne testy w Pythonie mogą być proste i skalowalne

  • Prosta składnia pozwala pisać testy bez rozbudowanych klas i konfiguracji.
  • Fixtures przygotowują dane, połączenia i zasoby potrzebne w wielu testach.
  • Parametryzacja sprawdza wiele wariantów wejścia bez kopiowania kodu.
  • Markery pomagają dzielić testy na szybkie, integracyjne i wymagające dodatkowego środowiska.
  • Integracja z CI umożliwia automatyczne blokowanie wadliwych zmian przed wdrożeniem.

Po co sięgać po pytest w automatyzacji testów

To narzędzie uruchamia testy zapisane jako zwykłe funkcje i wykorzystuje standardowe instrukcje assert do sprawdzania wyników. Dzięki temu pierwszy test można napisać bez tworzenia hierarchii klas, specjalnych metod setup ani dużej konfiguracji.

def calculate_total(price, tax):
    return price + price * tax

def test_calculate_total():
    assert calculate_total(100, 0.23) == 123

Test zapisany w pliku o nazwie zaczynającej się od test_ lub kończącej się na _test.py może zostać automatycznie wykryty. Uruchomienie całego zestawu sprowadza się do polecenia python -m pytest, a wynik od razu pokazuje, który przypadek się nie powiódł i jakie wartości doprowadziły do błędu.

W praktyce największą różnicę robi nie samo wykrywanie testów, ale czytelny komunikat o awarii. Zamiast ręcznie dodawać logowanie do każdej funkcji, otrzymuję porównanie wartości oczekiwanej i rzeczywistej. To szczególnie przydatne przy refaktoryzacji, kiedy kod wewnętrzny się zmienia, ale zachowanie aplikacji powinno pozostać takie samo.

Jak zbudować pierwszy zestaw testów

Najbezpieczniej zacząć od małych funkcji, które mają jednoznaczne wejście i wyjście. Test powinien sprawdzać jedną regułę biznesową, a nie od razu całą aplikację z bazą danych, siecią i systemem plików.

Układ testu oparty na trzech krokach

Dobry punkt wyjścia stanowi schemat Arrange, Act, Assert. Najpierw przygotowuję dane, potem wywołuję testowaną funkcję, a na końcu porównuję rezultat z oczekiwaniem.

def test_discount_for_regular_customer():
    price = 200
    discount = 0.10

    result = price - price * discount

    assert result == 180

Jeśli test wymaga wielu niezależnych warunków, lepiej podzielić go na kilka krótszych przypadków. Jeden długi test może wyglądać efektownie, ale po awarii trudno ustalić, która reguła przestała działać.

Konfiguracja i uruchamianie

Pakiet instaluję w środowisku wirtualnym projektu, a testy trzymam zwykle w katalogu tests. Przydatne jest także zapisanie ustawień w pliku konfiguracyjnym projektu, aby wszyscy członkowie zespołu korzystali z tych samych ścieżek, markerów i opcji uruchamiania.

  • uruchomienie całego zestawu przydaje się przed wysłaniem zmian do repozytorium,
  • uruchomienie pojedynczego pliku przyspiesza pracę nad konkretnym modułem,
  • uruchomienie jednego testu ułatwia szybkie sprawdzenie poprawki,
  • tryb bardziej szczegółowy pomaga znaleźć źródło problemu w dużym projekcie.

Nie zaczynałbym od testowania wszystkiego naraz. Lepiej mieć 20 krótkich i stabilnych testów dla najważniejszych reguł niż 100 przypadków zależnych od lokalnej bazy danych, zegara systemowego i kolejności wykonania.

Fixtures, parametry i markery bez powielania kodu

Gdy zestaw testów rośnie, kopiowanie tych samych danych szybko prowadzi do bałaganu. Framework rozwiązuje ten problem za pomocą fixtures, czyli funkcji przygotowujących zasoby używane przez testy. Mogą dostarczać obiekt użytkownika, tymczasowy katalog, klienta HTTP albo połączenie z testową bazą danych.

Fixtures porządkują przygotowanie środowiska

Dobrze zaprojektowana fixture ukrywa techniczne szczegóły i zwraca testowi tylko to, czego naprawdę potrzebuje. Jeżeli kilka przypadków korzysta z tego samego klienta API, nie tworzę go ręcznie w każdym teście. Dzięki temu zmiana konfiguracji odbywa się w jednym miejscu.

Trzeba jednak pilnować zakresu życia zasobu. Fixture tworzona dla każdego testu daje większą izolację, ale może działać wolniej. Zasób współdzielony przez cały moduł przyspiesza wykonanie, lecz zwiększa ryzyko, że jeden test pozostawi dane wpływające na kolejny.

Parametryzacja sprawdza wiele wariantów

Jeśli ta sama funkcja ma obsługiwać różne dane wejściowe, parametryzacja pozwala zachować jeden szablon testu i dostarczyć mu kilka zestawów wartości. To dobry sposób na sprawdzenie liczb granicznych, pustych napisów, błędnych formatów oraz poprawnych przypadków.

Przypadek Dane wejściowe Oczekiwany rezultat
standardowy 100 zł poprawne naliczenie podatku
wartość graniczna 0 zł brak błędu i wynik równy zero
niepoprawny format pusty tekst kontrolowany wyjątek

Ważne, aby każdy wariant miał czytelną nazwę. Sam numer przypadku niewiele mówi w raporcie, natomiast opis wskazujący na wartość graniczną od razu naprowadza na problem.

Przeczytaj również: Cypress real events - Kiedy używać natywnych interakcji?

Markery oddzielają różne rodzaje testów

Nie wszystkie testy powinny uruchamiać się przy każdej zmianie. Marker pozwala oznaczyć na przykład testy integracyjne, wolne, wymagające sieci albo zależne od systemu operacyjnego. W codziennej pracy uruchamiam najpierw szybki zestaw jednostkowy, a pełniejszą kontrolę zostawiam dla pipeline'u CI.

Markery nie powinny jednak służyć do ukrywania niestabilnych testów. Jeżeli przypadek regularnie kończy się błędem, oznaczanie go jako „wolny” czy „opcjonalny” tylko odkłada problem.

Schemat procesu CI/CD: zespół dostarcza kod, który przechodzi przez kontrolę wersji, commit, automatyczne testy (np. pytest), walidacje manualne i wydanie.

Jak połączyć testy z CI i kontrolą jakości

Automatyzacja zaczyna przynosić pełną wartość dopiero wtedy, gdy testy uruchamiają się bez ręcznego przypominania. W systemie ciągłej integracji każdy pull request może przejść przez zestaw kontroli obejmujący testy, formatowanie kodu, analizę statyczną i sprawdzenie typów.

Najpraktyczniejszy układ ma zwykle dwa poziomy. Szybkie testy jednostkowe uruchamiają się przy każdej zmianie, natomiast testy integracyjne i end-to-end mogą działać po połączeniu zmian z główną gałęzią albo według harmonogramu.

Rodzaj testu Co sprawdza Kiedy uruchamiać
jednostkowy pojedynczą funkcję lub klasę przy każdym commitcie
integracyjny współpracę kilku komponentów przy pull requeście lub przed wdrożeniem
end-to-end pełną ścieżkę użytkownika w głównym pipeline'ie lub cyklicznie

Warto ustalić prosty próg jakości. Na przykład zmiana nie może zostać scalona, jeśli testy zakończą się błędem, a nowe funkcje nie mają podstawowego pokrycia. Sam procent pokrycia nie jest jednak celem. 100% pokrycia słabymi testami może dawać mniejsze bezpieczeństwo niż 70% dobrze dobranych przypadków obejmujących krytyczne reguły.

Najczęstsze błędy i rozsądne granice automatyzacji

Początkujący często testują szczegóły implementacji zamiast zachowania aplikacji. Taki test psuje się po każdej refaktoryzacji, nawet gdy użytkownik nadal otrzymuje poprawny wynik. Ja preferuję sprawdzanie publicznego interfejsu funkcji lub modułu i ograniczam zaglądanie do jego wnętrza.

Drugim problemem są zależności zewnętrzne. Prawdziwa baza danych, zdalne API i aktualny czas mogą sprawić, że test będzie wolny albo losowo zawodny. W takich miejscach przydają się dane testowe, izolowane środowiska oraz mocki, czyli kontrolowane zamienniki prawdziwych usług.

  • nie używaj przypadkowych danych generowanych bez ustalonego ziarna,
  • nie pozwalaj testom modyfikować wspólnego stanu bez sprzątania,
  • nie wyciszaj ostrzeżeń i błędów tylko po to, aby pipeline był zielony,
  • nie twórz jednego testu obejmującego cały proces biznesowy, jeśli można rozdzielić go na mniejsze reguły.

To narzędzie nie zastąpi testów wydajności, bezpieczeństwa ani ręcznej oceny doświadczenia użytkownika. Jego mocna strona leży gdzie indziej. Bardzo dobrze pilnuje powtarzalnych reguł i daje zespołowi szybki sygnał, że zmiana naruszyła istniejące zachowanie.

Najlepszy pierwszy krok do stabilnej automatyzacji

Na początek wybierz jedną ważną funkcję, opisz jej poprawne i błędne przypadki, a potem uruchamiaj test przy każdej zmianie. Dopiero gdy podstawy są stabilne, dodawaj fixtures, parametryzację, markery i testy integracyjne.

Moim zdaniem największą wartość daje nie liczba napisanych przypadków, lecz ich czytelność, izolacja i szybki feedback. Dobrze zorganizowany zestaw testów staje się praktyczną dokumentacją projektu i pozwala rozwijać aplikację bez ciągłego lęku przed nieoczekiwanym błędem.

FAQ - Najczęstsze pytania

Pytest automatycznie wykrywa pliki zaczynające się od test_ lub kończące się na _test.py. Cały zestaw uruchomisz poleceniem python -m pytest, a podczas pracy możesz wskazać pojedynczy plik, test lub użyć bardziej szczegółowego trybu.

Fixtures warto stosować, gdy wiele testów korzysta z tych samych danych lub zasobów, takich jak klient API, katalog tymczasowy albo połączenie z bazą. Fixture tworzona dla każdego testu zapewnia większą izolację, natomiast zasób współdzielony przez moduł przyspiesza działanie, ale zwiększa ryzyko wpływu jednego testu na kolejny.

Pozwala zachować jeden szablon testu i uruchomić go dla wielu zestawów wartości. Dzięki temu można sprawdzić między innymi przypadki standardowe, wartości graniczne, puste napisy i błędne formaty bez kopiowania kodu. Warto nadawać wariantom czytelne nazwy, aby raport wskazywał rodzaj problemu.

Szybkie testy jednostkowe powinny uruchamiać się przy każdej zmianie. Testy integracyjne można wykonywać przy pull requeście lub przed wdrożeniem, a testy end-to-end w głównym pipeline lub cyklicznie. Zmiana może być blokowana, gdy testy kończą się błędem, ale sam procent pokrycia nie zastępuje dobrze dobranych przypadków.

Oceń artykuł

Ocena: 0.00 Liczba głosów: 0

Tagi:

pytest fixtures parametryzacja markery ci

Udostępnij artykuł

Juliusz Król

Juliusz Król

Nazywam się Juliusz Król i od 4 lat zajmuję się automatyzacją testów oraz zapewnieniem jakości oprogramowania. Moje zainteresowanie tymi tematami zrodziło się z potrzeby zrozumienia, jak technologia może wspierać procesy tworzenia oprogramowania i jak kluczowe jest zapewnienie jego wysokiej jakości. W swoich tekstach skupiam się na praktycznych rozwiązaniach, które pomagają innym w efektywnym wdrażaniu automatyzacji oraz w rozwiązywaniu problemów związanych z jakością. Dzięki mojemu doświadczeniu staram się przekazywać wiedzę w sposób przystępny i zrozumiały, porównując różne podejścia oraz analizując aktualne trendy w branży. Zawsze dbam o to, aby moje materiały były rzetelne, aktualne i pomocne dla czytelników, którzy pragną rozwijać swoje umiejętności w obszarze QA.

Napisz komentarz