PYTANIA? DZWOŃ: +48 55 646 44 88

SYSINFO

SYSINFO

Firma informatyczna działająca od 1999 roku.

+48 55 645 44 88
Email: info@sysinfo.pl

SYSINFO Sławomir Babiński
Kwidzyński Park Przemysłowo Technologiczny, Górki 3A, biuro D2.03, 82-500 KWIDZYN

Open in Google Maps
  • OFERTA
    • USŁUGI i WDROŻENIA
      • Obsługa Informatyczna Firm ★★★★★
      • Serwery Microsoft Windows
      • Wdrożenia
      • Konsulting
    • SPRZĘT i DOSTAWA
      • Serwery Dell, Fujitsu, HP, Lenovo
      • Komputery i osprzęt Dell, Fujitsu, HP, Lenovo
      • FORTINET – Firewalle Następnej Generacji NGFW
      • QNAP – Ochrona Danych
    • OPROGRAMOWANIE
      • InsERT GT/nexo – komplet do prowadzenia firmy
      • Ochrona antywirusowa ESET
  • PRACA
  • O NAS
  • RODO
  • SYSINFO // blog
Pomoc Zdalna

Testy jednostkowe w Pythonie – wprowadzenie, narzędzia i dobre praktyki

Testy jednostkowe w Pythonie – wprowadzenie, narzędzia i dobre praktyki

by sbabinski / 22.04.2025 / Published in programowanie

Wyobraź sobie, że piszesz skomplikowaną aplikację i po wprowadzeniu drobnej zmiany nagle coś przestaje działać. Testy jednostkowe to właśnie sposób, aby przed takimi niespodziankami się uchronić. Test jednostkowy to fragment kodu, który automatycznie sprawdza poprawność działania innego, małego fragmentu systemu (np. pojedynczej funkcji lub metody) w pełnej izolacji od reszty programu [1]. Dzięki temu programista może szybko zweryfikować, czy dana część kodu spełnia założenia.

Dlaczego testy jednostkowe są tak ważne? Korzyści jest wiele:

  • Wczesne wykrywanie błędów: Pozwalają znaleźć bugi na wczesnym etapie rozwoju, zanim trafią one na produkcję i wyrządzą szkody [1].
  • Bezpieczna modyfikacja kodu: Ułatwiają zmiany i refaktoryzację – mając solidny zestaw testów, można śmiało modyfikować kod, mając pewność, że nie wprowadzono nowych błędów [1].
  • Dokumentacja zachowania: Dobrze napisane testy służą jako dokumentacja – pokazują, jak funkcje powinny działać w różnych scenariuszach (w tym brzegowych), co pomaga nowym członkom zespołu zrozumieć kod [1].
  • Mniej błędów na produkcji: Testowanie każdego komponentu z osobna zmniejsza ryzyko wystąpienia błędów w finalnej aplikacji i zwiększa ogólną jakość projektu [1].

W praktyce testy jednostkowe stały się codziennością pracy programisty – utrzymanie dużych, złożonych projektów byłoby niezwykle trudne bez automatycznych testów sprawdzających poprawność działania poszczególnych części aplikacji. W ekosystemie Pythona istnieje wiele narzędzi wspierających tworzenie testów jednostkowych. Poniżej przyjrzymy się najważniejszym z nich: wbudowanemu modułowi unittest oraz popularnym bibliotekom pytest i nose2. Następnie porównamy je, zobaczymy proste przykłady użycia oraz omówimy dobre praktyki pisania testów.

Najważniejsze narzędzia do testów jednostkowych w Pythonie

Python oferuje zarówno wbudowane, jak i zewnętrzne frameworki do tworzenia testów. Omówmy trzy najczęściej spotykane rozwiązania.

unittest

unittest to podstawowy framework do testów jednostkowych dostarczany w standardowej bibliotece Pythona [2]. Jego interfejs został zainspirowany biblioteką JUnit znaną z Javy, przez co bywa nazywany podejściem xUnit. Korzystając z unittest, piszemy klasy dziedziczące po unittest.TestCase i w ich metodach sprawdzamy oczekiwane rezultaty za pomocą specjalnych asercji (np. self.assertEqual, self.assertTrue itd.). Framework dba o wykonanie tzw. fixture, czyli konfiguracji przed testem i sprzątania po teście – można to osiągnąć definiując metody setUp/tearDown (dla każdego testu) lub setUpClass/tearDownClass (dla całej klasy testowej).

Przykład prostego testu z użyciem unittest może wyglądać tak:

# Kod, który testujemy
def dodaj(a, b):
    return a + b

# Testy jednostkowe z użyciem unittest
import unittest

class DodawanieTestCase(unittest.TestCase):
    def test_dodaj_dodatnie(self):
        self.assertEqual(dodaj(2, 3), 5)

    def test_dodaj_zero(self):
        self.assertEqual(dodaj(5, 0), 5)

if __name__ == '__main__':
    unittest.main()

W powyższym kodzie zdefiniowaliśmy funkcję dodaj oraz klasę testową z dwoma przypadkami testowymi. Każdy test to oddzielna metoda zaczynająca się od test_. Na końcu używamy unittest.main() do uruchomienia testów – spowoduje to automatyczne wykonanie wszystkich metod testowych i raport wyników. Alternatywnie, testy można uruchamiać poleceniem w konsoli: python -m unittest (framework sam znajdzie wszystkie klasy dziedziczące po TestCase).

unittest jest prosty i dostępny od ręki (brak dodatkowych instalacji), co jest jego wielką zaletą. Pisanie testów w czystym unittest bywa jednak nieco rozwlekłe – wymaga utworzenia klasy, asercje mają formę wywołań metod, a przed testami często trzeba zadbać o ręczne przygotowanie danych. Z tego powodu powstały biblioteki, które rozszerzają możliwości testowania w Pythonie, upraszczając składnię i dodając dodatkowe funkcjonalności.

pytest

pytest to obecnie jeden z najpopularniejszych frameworków testowych w środowisku Pythona [5]. Jest to biblioteka zewnętrzna (należy ją zainstalować z PyPI), ale jej rosnąca popularność wynika z bardzo wygodnej i przejrzystej składni oraz bogatego zestawu funkcji. Podstawowa różnica w stosunku do unittest polega na tym, że zamiast pisać klasy testowe, w pytest możemy tworzyć po prostu funkcje zaczynające się od test_. Do sprawdzania wyników używamy zwykłego assert wbudowanego w język – pytest przechwytuje te asercje i w razie błędu wyświetla czytelny komunikat wskazujący, jaka wartość była oczekiwana, a jaka się pojawiła [5].

# Kod, który testujemy
def dodaj(a, b):
    return a + b

# Testy jednostkowe z użyciem pytest
def test_dodaj_dodatnie():
    assert dodaj(2, 3) == 5

def test_dodaj_zero():
    assert dodaj(5, 0) == 5

Powyższy kod to kompletny zestaw testów dla funkcji dodaj. Nie potrzebujemy żadnej klasy ani dodatkowych dekoratorów – pytest automatycznie znajdzie funkcje zaczynające się od test_ w plikach nazwanych test_*.py (lub *_test.py) i je uruchomi. Wystarczy wywołać w terminalu polecenie pytest (po uprzedniej instalacji biblioteki). Raport z testów będzie zawierał listę testów i informację, czy każdy z nich się powiódł, a w razie porażki pokaże dokładnie, gdzie wystąpiła różnica.

Dlaczego pytest zyskał tak dużą popularność? Jest ku temu kilka powodów. Po pierwsze, składnia testów jest zwięzła – mniej kodu oznacza, że łatwiej napisać i utrzymać duży zestaw testów [5]. Po drugie, pytest oferuje mechanizm fixture do łatwego przygotowania i sprzątania danych testowych przed wieloma testami. Fixture’y definiuje się jako funkcje z dekoratorem @pytest.fixture lub po prostu jako parametry funkcji testowej (pytest automatycznie wstrzykuje odpowiednie obiekty na podstawie nazwy parametru). To pozwala eliminować duplikację kodu inicjalizacyjnego i utrzymać testy w czystości. Po trzecie, istnieje ogromny ekosystem wtyczek – społeczność stworzyła dziesiątki pluginów rozszerzających możliwości pytest (np. mierzenie pokrycia kodu, parametryzacja testów, integracja z frameworkami webowymi, itp.) [5]. Wreszcie, pytest potrafi bez problemu uruchamiać testy napisane w stylu unittest lub nawet w nose, co ułatwia migrację istniejących projektów do pytest bez konieczności przepisywania wszystkich testów od zera.

nose2

nose2 to następca niegdyś popularnej biblioteki nose. Nose (pierwszej generacji) przestał być aktywnie rozwijany, więc społeczność stworzyła nose2 jako unowocześnioną alternatywę. Pod względem filozofii nose2 jest bliski unittest – korzysta z tych samych mechanizmów (klas TestCase, funkcji setUp/tearDown itp.), ale dodaje od siebie automatyczne wyszukiwanie testów i bogaty system wtyczek. Można powiedzieć, że nose2 rozszerza unittest, aby uczynić go bardziej przyjaznym i konfigurowalnym [4].

Przykładowo, nose2 domyślnie przeszukuje projekt w poszukiwaniu plików i funkcji testowych. Można pisać testy w stylu zbliżonym do pytest (czyli same funkcje z assert) albo w klasycznym stylu unittest – nose2 obsłuży oba podejścia. Uruchomienie testów jest proste: w konsoli wystarczy wpisać nose2, a framework sam znajdzie i uruchomi wszystko, co należy.

Minimalny przykład testu pod nose2 może wyglądać identycznie jak w pytest:

# Kod, który testujemy
def dodaj(a, b):
    return a + b

# Test jednostkowy z użyciem nose2
def test_dodaj():
    assert dodaj(2, 2) == 4

Plik o nazwie np. test_moj_modul.py z powyższą funkcją zostanie wykryty i wykonany przez nose2 po uruchomieniu komendy nose2. Alternatywnie, możemy użyć stylu unittest (nose2 wykryje klasy TestCase tak samo jak pytest). Dodatkową zaletą nose2 jest system wtyczek pozwalający m.in. parametryzować testy czy oznaczać je tagami. Przykładowo nose2 udostępnia dekorator @params do łatwego zdefiniowania wielu wariantów testu z różnymi danymi.

Warto zauważyć, że nose2 nie zdobył tak dużej popularności jak pytest. Sam autor nose2 w dokumentacji projektu zachęca, aby rozważyć pytest dla nowych projektów, ze względu na większą liczbę maintainerów i szerszą społeczność [4]. Nose2 bywa jednak użyteczny, jeśli mamy istniejący zbiór testów nose/unittest i chcemy go stopniowo rozszerzać o dodatkowe możliwości bez całkowitej migracji na inny framework.

Porównanie frameworków testowych: unittest, pytest i nose2

Każde z opisanych narzędzi ma swoje mocne i słabe strony. Wybór zależy od potrzeb projektu i preferencji zespołu. Poniżej podsumowanie zalet i wad, a także wskazówki, kiedy które narzędzie może być najlepszym wyborem.

  • unittest – Zalety: Wbudowany w język (brak dodatkowych zależności), dobrze udokumentowany i wspierany przez narzędzia (IDE, CI) od lat. Jest prosty i uniwersalny – dostępny zawsze tam, gdzie Python. Wady: Wymaga więcej pisania kodu (szablon klasy, osobne metody), co czyni testy nieco bardziej rozwlekłymi. Brakuje nowoczesnych udogodnień wprost (np. parametryzacja testów czy zaawansowane fixture – choć można to osiągnąć ręcznie). Kiedy używać: Gdy zależy nam na minimalizmie i kompatybilności – np. w projekcie, gdzie nie chcemy instalować dodatkowych bibliotek, albo gdy zaczynamy przygodę z testami i chcemy zrozumieć podstawy mechanizmów testujących. unittest sprawdzi się też w środowiskach o wysokich restrykcjach, gdzie dozwolone są tylko biblioteki standardowe.
  • pytest – Zalety: Bardzo czytelna i zwięzła składnia, mniej ceremonii przy pisaniu testów. Bogaty zestaw funkcji wprost z pudełka (fixture, asercje z informacją o różnicy, wsparcie dla testów parametrów itp.). Ogromna społeczność i rozszerzalność – setki pluginów umożliwiają dostosowanie frameworka do własnych potrzeb [5]. Duża popularność przekłada się na wiele poradników, artykułów i wsparcie online. Wady: Wymaga instalacji (choć to zazwyczaj nie problem). Dla osób przyzwyczajonych do stylu xUnit (jak unittest) początkowo podejście bezklasowe może wydawać się nietypowe. Niekiedy bogactwo funkcji (np. rozbudowane fixture) może wprowadzać dodatkową abstrakcję, którą trzeba zrozumieć. Kiedy używać: W większości nowych projektów pytest będzie dobrym wyborem – pozwala pisać testy szybciej i czytelniej, co sprzyja pokryciu kodu testami. Jest idealny dla średnich i dużych projektów, gdzie wygoda i możliwości frameworka oszczędzą dużo czasu. Także w małych projektach docenisz prostotę składni. Jeśli Twój projekt używa już unittest, możesz stopniowo przechodzić na pytest – narzędzie to uruchomi istniejące testy unittests bez problemu, więc migracja może być płynna.
  • nose2 – Zalety: Pozwala wykorzystać znajomość unittest, dodając do niego automatyczne odkrywanie testów i pluginy. Obsługuje zarówno styl pisania testów jak w unittest, jak i prostsze funkcje z assert (podobnie do pytest). Wprowadza udogodnienia, takie jak dekoratory do parametryzacji testów, bez potrzeby zmiany frameworka na zupełnie inny. Wady: Mniejsza społeczność i mniej aktywny rozwój niż pytest [4]. Mniej materiałów i wsparcia w sieci – większość programistów Python skupia się dziś na pytest lub unittest. Ponieważ nose2 jest kontynuatorem nose, może być postrzegany jako rozwiązanie tymczasowe (bridge) dla projektów, które historycznie używały nose. Kiedy używać: Głównie wtedy, gdy masz istniejący kod testów oparty o nose i chcesz korzystać z niego dalej, zyskując kompatybilność z nowszym Pythonem. Jeśli startujesz nowy projekt, raczej wybierz pytest lub unittest – społeczność nose2 sama wskazuje pytest jako lepszy wybór dla nowych przedsięwzięć [4]. Niemniej, nose2 może się sprawdzić, gdy z jakiegoś powodu nie możesz użyć pytest, a chcesz czegoś więcej niż oferuje goły unittest (np. potrzebujesz pluginów lub prostszego uruchamiania testów).

Rady praktyczne i dobre praktyki

Niezależnie od wybranego narzędzia, warto trzymać się pewnych uniwersalnych zasad pisania testów jednostkowych. Oto kilka wskazówek, które pomogą utrzymać testy efektywnymi i bezproblemowymi:

  • Izoluj testy od zależności zewnętrznych: Test jednostkowy powinien testować logikę w izolacji. Unikaj bezpośredniego korzystania z baz danych, sieci, plików czy innych zasobów zewnętrznych w testach jednostkowych. Jeśli funkcja komunikuje się z czymś zewnętrznym, użyj mocków lub stubs (np. moduł unittest.mock) aby odizolować tę zależność. Dzięki temu testy będą szybkie i deterministyczne – ich wynik nie będzie zależał od środowiska czy czynników losowych [6].
  • Utrzymuj niezależność testów: Każdy test powinien móc być uruchomiony samodzielnie z takim samym wynikiem. Nie polegaj na efekcie ubocznym innego testu (np. danych zapisanych przez inny test). Jeśli różne testy wymagają podobnej konfiguracji, użyj mechanizmów do współdzielenia stanu (fixture, wspólne setUp itp.), ale zadbaj, by zmiany w jednym teście nie wpływały na inny. Niezależność ułatwia debugowanie – gdy test padnie, wiesz, że przyczyna leży w tym, co on sprawdza, a nie w wcześniejszym stanie środowiska.
  • Czytelność przede wszystkim: Pisz testy tak, by były zrozumiałe. Nazwy funkcji testowych/metod powinny jasno mówić, co jest sprawdzane (np. test_dodaj_zwraca_sume jest lepsze niż test_dodaj czy test1). Gdy ktoś inny czyta Twój test, powinien szybko pojąć, jaka funkcjonalność jest weryfikowana. Unikaj duplikowania kodu w testach – jeśli kilka testów ma ten sam przygotowawczy fragment kodu, rozważ przeniesienie go do funkcji pomocniczej lub fixture. Dobre praktyki w nazewnictwie i strukturze testów znacznie ułatwiają ich utrzymanie [6].
  • Jeden test – jeden scenariusz: Staraj się, by pojedynczy test sprawdzał jedną konkretną rzecz. Jeśli test robi zbyt wiele (np. najpierw sprawdza wynik funkcji dla danych poprawnych, potem dla błędnych, a na końcu jeszcze efekt uboczny), podziel go na mniejsze. Mniejszy zakres ułatwia znalezienie przyczyny błędu, gdy test się wyłoży. Reguła ta dotyczy także asercji – jedna lub dwie asercje na test to zwykle maksimum, jakie jest potrzebne. Gdy potrzebujesz więcej, możliwe, że lepiej napisać osobny test dla innego aspektu funkcji.
  • Testuj przypadki brzegowe: Dobrze przemyśl zestaw przypadków testowych. Oprócz typowych scenariuszy (“happy path”), uwzględnij wartości skrajne, nietypowe lub błędne dane wejściowe, które funkcja powinna obsłużyć (np. null/None, pusty string, bardzo duża liczba, itp.). Testy są po to, by złapać błędy tam, gdzie program może zachować się nieoczekiwanie – pokrycie różnych ścieżek wykonania zwiększa szanse na wychwycenie ukrytych problemów.
  • Dbaj o szybkość i powtarzalność: Zestaw testów jednostkowych powinien działać szybko – ideałem jest możliwość uruchamiania wszystkich testów po każdej drobnej zmianie kodu. Unikaj więc opóźnień typu time.sleep() w testach, nie wykonuj tysięcy iteracji tam, gdzie wystarczy kilka, itp. Jeśli testy są wolne, programiści będą je rzadziej uruchamiać, tracąc korzyści z szybkiej informacji zwrotnej [6]. Ponadto testy nie powinny być flakujące (niestabilne) – ten sam kod testu uruchomiony wiele razy na tym samym kodzie aplikacji musi dawać ten sam rezultat. Jeżeli zdarza się, że test raz przechodzi, a raz nie (np. zależy od aktualnej daty lub losowo generowanej liczby), trzeba to poprawić, np. poprzez zmockowanie zegara lub ustawienie ziarna generatora liczb losowych.

Na koniec warto wspomnieć o podejściu TDD (Test-Driven Development) – czyli pisaniu testów przed kodem. Choć nie każdy projekt stosuje TDD, to spróbowanie tej metody może nauczyć Cię patrzeć na kod z perspektywy testowalności. Nawet jeśli nie praktykujesz “najpierw testy”, staraj się pisać testy równolegle z implementacją nowych funkcji, a nie dopiero tuż przed releasem. Im wcześniej napiszesz test dla danej funkcjonalności, tym pewniej możesz rozwijać projekt.

Podsumowanie

Testy jednostkowe w Pythonie to potężne narzędzie w rękach programisty – pozwalają w automatyczny sposób dbać o jakość kodu i zapobiegać regresjom. W tym artykule omówiliśmy, czym są testy jednostkowe i dlaczego warto w nie inwestować, a także przyjrzeliśmy się trzem frameworkom: wbudowanemu unittest oraz zewnętrznym pytest i nose2. Każde z nich ma nieco inną filozofię i zastosowanie, przy czym obecnie to pytest wiedzie prym jako najwygodniejsze rozwiązanie dla większości projektów. Nie znaczy to jednak, że należy ignorować pozostałe – kluczowe jest dobranie narzędzia odpowiedniego do kontekstu oraz konsekwentne stosowanie dobrych praktyk przy pisaniu testów.

Mamy nadzieję, że przedstawione porównanie i porady zachęcą Cię do jeszcze szerszego stosowania testów jednostkowych. Wdrażając testy do codziennej pracy, zyskasz pewność, że Twoje oprogramowanie działa poprawnie, a wprowadzanie zmian nie będzie budzić strachu przed popsuciem istniejących funkcjonalności. Pamiętaj – im lepsze testy, tym spokojniejsze noce programisty!

Przypisy:

  1. ProgramistaJava – Co to są testy jednostkowe? (2025) – Definicja testów jednostkowych i ich znaczenie w procesie tworzenia oprogramowania. https://programistajava.pl/2025/04/12/co-to-sa-testy-jednostkowe/
  2. Dokumentacja Python 3 – unittest — Unit testing framework – Oficjalna dokumentacja modułu unittest w bibliotece standardowej Pythona. https://docs.python.org/3/library/unittest.html
  3. Dokumentacja pytest – Strona dokumentacji frameworka pytest (wersja online). https://docs.pytest.org/en/latest/
  4. Dokumentacja nose2 – Welcome to nose2 – Oficjalna dokumentacja biblioteki nose2 (następcy nose), zawierająca m.in. porównanie nose2 z pytest i unittest. https://docs.nose2.io/en/latest/
  5. PyCharm Blog – Pytest vs. Unittest: Which Is Better? (Mar 2024) – Porównanie frameworków pytest i unittest, omówienie ich zalet, wad oraz popularności wśród programistów Pythona. https://blog.jetbrains.com/pycharm/2024/03/pytest-vs-unittest
  6. Bulldogjob – Pisanie testów jednostkowych w Pythonie – dobre praktyki – Artykuł omawiający typowe problemy i najlepsze praktyki przy tworzeniu testów jednostkowych w Pythonie. https://bulldogjob.pl/readme/pisanie-testow-jednostkowych-w-pythonie-dobre-praktyki
  • Tweet

About sbabinski

Kategorie

  • AI (1)
  • programowanie (1)
  • sieci (2)
  • www (2)

Ostanie wpisy

  • Testy jednostkowe w Pythonie – wprowadzenie, narzędzia i dobre praktyki
  • Model Context Protocol (MCP) – uniwersalny standard zarządzania kontekstem w AI
  • Teltonika RUTX50 – możliwości w zastosowaniach profesjonalnych
  • Elementor vs Bricks Builder – Porównanie (marzec 2025)
  • Elementor vs Breakdance – który page builder wypada lepiej?
  • pfSense vs OPNsense – porównanie routerów

Siedziba

SYSINFO Sławomir Babiński
Kwidzyński Park Przemysłowo-Technologiczny
Górki 3A, 82-500 Kwidzyn
Biuro D2.03 (wejście główne, I piętro)

Pracujemy od poniedziałku do piątku w godzinach 8.00 – 16.00.

  • Tel. +48 55 646 44 88
  • Email: biuro@sysinfo.pl

Skanuj telefonem

qr_sysinfo
SYSINFO

©All rights reserved

TOP