Automatyzacja testów w GitHub Actions – CI/CD dla projektów Python

Kacper Sieradziński
Kacper Sieradziński
5 lipca 2025Edukacja4 min czytania

Pisanie testów to fundament jakości kodu, ale automatyczne uruchamianie testów to klucz do utrzymania tej jakości w czasie. GitHub Actions pozwala uruchamiać testy przy każdym commicie, generować raporty coverage, blokować merge requesty z błędami i automatycznie wdrażać kod do produkcji. W tym artykule nauczysz się konfigurować CI/CD pipeline dla projektów Python, automatyzować testy i coverage, oraz integrować z zewnętrznymi narzędziami zapewniającymi jakość kodu.

Obraz główny Automatyzacja testów w GitHub Actions – CI/CD dla projektów Python

Czym jest CI/CD?

CI/CD (Continuous Integration / Continuous Deployment) to praktyka automatycznego:

  • CI (Continuous Integration) – integracja zmian kodu, uruchamianie testów, sprawdzanie jakości
  • CD (Continuous Deployment) – automatyczne wdrażanie przetestowanego kodu do produkcji

Korzyści z CI/CD:

  • 🔍 Wczesne wykrywanie błędów – testy uruchamiane przy każdym commicie
  • 🚀 Szybsze wdrożenia – automatyzacja procesu release
  • 🛡️ Bezpieczeństwo – kod jest testowany przed wdrożeniem
  • 📊 Przejrzystość – każdy widzi status testów i wdrożeń
  • ⏱️ Oszczędność czasu – nie trzeba ręcznie uruchamiać testów

Jeśli dopiero zaczynasz z testowaniem, sprawdź najpierw wprowadzenie do unittest lub testowanie z pytest.

Wprowadzenie do GitHub Actions

GitHub Actions to platforma CI/CD wbudowana w GitHub. Używa plików YAML do definiowania workflow (procesów automatyzacji).

Struktura workflow

Workflow w GitHub Actions składa się z:

  • Events – kiedy workflow ma się uruchomić (push, pull_request, etc.)
  • Jobs – zadania do wykonania (np. testy, build)
  • Steps – kroki w każdym job (np. instalacja, uruchomienie testów)
  • Actions – gotowe komponenty (np. setup-python)

Gdzie umieścić workflow?

Workflow definiuje się w plikach YAML w katalogu:

Bash
1 .github/workflows/

Przykład struktury:

Bash
1 2 3 4 5 6 7 8 9 projekt/ ├── .github/ │ └── workflows/ │ ├── tests.yml │ ├── coverage.yml │ └── deploy.yml ├── src/ ├── tests/ └── README.md

Podstawowy workflow dla testów Python

Stwórzmy podstawowy workflow do uruchamiania testów:

YAML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 name: Tests on: push: branches: [main, develop] pull_request: branches: [main, develop] jobs: test: runs-on: ubuntu-latest strategy: matrix: python-version: ['3.10', '3.11', '3.12'] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install pytest pytest-cov - name: Run tests run: | pytest --cov=src --cov-report=xml --cov-report=term

Wyjaśnienie workflow

  1. name – nazwa workflow (wyświetlana w GitHub)
  2. on – events uruchamiające workflow
  • push – przy pushu do określonych branchy
  • pull_request – przy tworzeniu/aktualizacji PR
  1. jobs – lista zadań
  2. runs-on – system operacyjny (ubuntu-latest, windows-latest, macos-latest)
  3. strategy.matrix – uruchomienie testów na wielu wersjach Pythona
  4. steps – kroki wykonania:
  • checkout@v3 – pobranie kodu
  • setup-python@v4 – instalacja Pythona
  • Instalacja zależności i uruchomienie testów

Zaawansowany workflow z caching

Optymalizacja czasu wykonania dzięki cache'owaniu zależności:

YAML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 name: Tests on: push: branches: [main, develop] pull_request: branches: [main, develop] jobs: test: runs-on: ubuntu-latest strategy: matrix: python-version: ['3.10', '3.11', '3.12'] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} cache: 'pip' cache-dependency-path: | requirements.txt - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install pytest pytest-cov flake8 black - name: Lint with flake8 run: | flake8 src tests --count --select=E9,F63,F7,F82 --show-source --statistics flake8 src tests --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Format check with black run: | black --check src tests - name: Run tests run: | pytest --cov=src --cov-report=xml --cov-report=term --cov-report=html -v - name: Upload coverage reports uses: codecov/codecov-action@v3 with: file: ./coverage.xml flags: unittests name: codecov-umbrella

Workflow z warunkowym deploymentem

Automatyczne wdrożenie tylko gdy testy przejdą:

YAML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 # .github/workflows/deploy.yml name: Deploy on: push: branches: [main] workflow_run: workflows: ["Tests"] types: - completed jobs: deploy: runs-on: ubuntu-latest if: > github.event_name == 'push' || (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.11' - name: Install dependencies run: | pip install -r requirements.txt - name: Deploy to production run: | echo "Deploying to production..." # Twoje komendy deploymentu

Testowanie z bazą danych

Testy wymagające bazy danych:

YAML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 # .github/workflows/tests-db.yml name: Tests with Database on: push: branches: [main] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest services: postgres: image: postgres:15 env: POSTGRES_USER: testuser POSTGRES_PASSWORD: testpass POSTGRES_DB: testdb options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 ports: - 5432:5432 redis: image: redis:7 options: >- --health-cmd "redis-cli ping || exit 1" --health-interval 10s --health-timeout 5s --health-retries 5 ports: - 6379:6379 steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.11' cache: 'pip' - name: Install dependencies run: | pip install -r requirements.txt pip install pytest pytest-cov - name: Run tests env: DATABASE_URL: postgresql://testuser:testpass@localhost:5432/testdb REDIS_URL: redis://localhost:6379 run: | pytest --cov=src

Publikowanie artefaktów

Zapisywanie wyników testów i coverage jako artefaktów:

YAML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 - name: Generate coverage report run: pytest --cov=src --cov-report=html --cov-report=xml - name: Upload coverage HTML uses: actions/upload-artifact@v3 with: name: coverage-report path: htmlcov/ - name: Upload test results if: always() uses: actions/upload-artifact@v3 with: name: test-results path: test-results.xml

Status checks i branch protection

Aby wymusić, że PR musi mieć przechodzące testy:

  1. Przejdź do Settings → Branches
  2. Dodaj rule dla branch (np. main)
  3. Zaznacz: "Require status checks to pass before merging"
  4. Wybierz workflow do wymagania

Wtedy PR nie może być zmergowany, dopóki workflow nie przejdzie.

Workflow dla Django

Specjalny workflow dla projektów Django:

YAML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 # .github/workflows/django.yml name: Django CI on: push: branches: [main] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest services: postgres: image: postgres:15 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: testdb options: >- --health-cmd "pg_isready -U postgres -d testdb" --health-interval 10s --health-timeout 5s --health-retries 5 ports: - 5432:5432 steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.11' cache: 'pip' - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install pytest pytest-django pytest-cov - name: Run migrations env: DATABASE_URL: postgresql://postgres:postgres@localhost:5432/testdb run: | python manage.py migrate - name: Run tests env: DATABASE_URL: postgresql://postgres:postgres@localhost:5432/testdb SECRET_KEY: test-secret-key run: | pytest --cov=. --cov-report=xml

Workflow dla FastAPI

Workflow dla projektów FastAPI:

YAML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 # .github/workflows/fastapi.yml name: FastAPI CI on: push: branches: [main] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest strategy: matrix: python-version: ['3.10', '3.11', '3.12'] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} cache: 'pip' cache-dependency-path: | requirements.txt - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install pytest pytest-asyncio httpx pytest-cov - name: Run tests run: | pytest --cov=src --cov-report=xml -v - name: Upload coverage uses: codecov/codecov-action@v3 with: file: ./coverage.xml

Integracja z Codecov

Codecov śledzi coverage w czasie i pokazuje trendy:

  1. Zarejestruj się na codecov.io
  2. Dodaj repozytorium
  3. Skopiuj token
  4. Dodaj token jako secret w GitHub: Settings → Secrets → Actions
  5. Użyj w workflow:
YAML
1 2 3 4 5 6 7 - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: token: ${{ secrets.CODECOV_TOKEN }} file: ./coverage.xml flags: unittests name: codecov-umbrella

Workflow z matrix dla wielu środowisk

Testowanie na wielu systemach i wersjach:

YAML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 # .github/workflows/matrix-test.yml name: Matrix Tests on: push: branches: [main] pull_request: branches: [main] jobs: test: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] python-version: ['3.10', '3.11', '3.12'] exclude: - os: windows-latest python-version: '3.12' # Wykluczenie kombinacji steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} cache: 'pip' - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install pytest - name: Run tests run: pytest

Najlepsze praktyki

1. Cache'uj zależności

YAML
1 2 3 - uses: actions/setup-python@v4 with: cache: 'pip'# Automatyczne cache'owanie pip

2. Używaj matrix dla wielu wersji

Testuj na różnych wersjach Pythona i systemach operacyjnych.

3. Rozdzielaj zadania

Oddzielne joby dla linting, testów i deploymentu pozwalają na równoległe wykonanie.

4. Używaj conditionals

YAML
1 2 3 - name: Deploy if: github.ref == 'refs/heads/main' && github.event_name == 'push' run: echo "Deploying..."

5. Zabezpieczaj sekrety

Nigdy nie commituj tokenów czy haseł. Używaj GitHub Secrets.

6. Zapisuj artefakty

Używaj upload-artifact do zapisywania raportów coverage, logów, etc.

7. Ustaw timeouty

YAML
1 2 3 jobs: test: timeout-minutes: 30

8. Powiadomienia

YAML
1 2 3 4 5 - name: Notify on failure if: failure() run: | # Wyślij powiadomienie (email, Slack, Discord, etc.) echo "Tests failed!"

Debugowanie workflow

Włączanie debugowania

YAML
1 2 3 env: ACTIONS_STEP_DEBUG: true ACTIONS_RUNNER_DEBUG: true

Sprawdzanie logów

Wszystkie logi są dostępne w zakładce "Actions" w GitHub.

Lokalne testowanie

Użyj act do lokalnego uruchamiania workflow:

Bash
1 act push

Podsumowanie

GitHub Actions to potężne narzędzie do automatyzacji testów i deploymentu. Kluczowe punkty:

  • ✅ Workflow definiuje się w plikach YAML w .github/workflows/
  • ✅ Używaj matrix do testowania na wielu wersjach/environmentach
  • ✅ Cache'uj zależności dla szybszego wykonania
  • ✅ Rozdzielaj zadania na osobne joby
  • ✅ Integruj z narzędziami jak Codecov
  • ✅ Zabezpieczaj sekrety przez GitHub Secrets
  • ✅ Używaj branch protection do wymuszania status checks

CI/CD to fundament nowoczesnego developmentu. Automatyzacja testów gwarantuje, że kod jest zawsze przetestowany przed wdrożeniem.

Co dalej?

Teraz, gdy znasz GitHub Actions, zastosuj to w praktyce:

Zacznij od prostego workflow i stopniowo dodawaj zaawansowane funkcje. Automatyzacja testów to inwestycja, która szybko się zwraca!