Skip to content

Testing Guide

This guide covers testing strategies, IDE setup, and debugging for the Freeze Design webshop.

Test Pyramid

        ┌───────────┐
        │   E2E     │  ← Few, slow, high confidence
        ├───────────┤
        │Integration│  ← Medium number
        ├───────────┤
        │   Unit    │  ← Many, fast, focused
        └───────────┘

Backend Testing (Django)

Run Tests

cd backend
python manage.py test

# Run specific app tests
python manage.py test apps.payments
python manage.py test apps.invoices
python manage.py test apps.orders

# Run specific test modules
python manage.py test apps.payments.tests.test_webhook
python manage.py test apps.payments.tests.test_refund_service
python manage.py test apps.invoices.tests.test_invoice_service

# Run with verbosity
python manage.py test --verbosity=2

With Coverage

CI runs the backend suite via pytest (configured in backend/pyproject.toml): parallel (-n auto --dist=loadscope) with coverage and a 60% fail-under threshold.

# Same as CI
pytest

# Or via Django's test runner
coverage run manage.py test
coverage report
coverage html  # Open htmlcov/index.html

Test Structure

# tests/test_products.py
from django.test import TestCase
from rest_framework.test import APITestCase

class ProductAPITests(APITestCase):
    def setUp(self):
        self.product = Product.objects.create(
            name='Test Product',
            base_price=29.95
        )

    def test_list_products(self):
        response = self.client.get('/api/products/')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.data['results']), 1)

    def test_product_detail(self):
        response = self.client.get(f'/api/products/{self.product.slug}/')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data['name'], 'Test Product')

Frontend Testing

Unit Tests (Jest)

cd frontend
npm run test
npm run test:coverage
npm run test:watch          # Watch mode (auto-rerun)
npm test TeamOrderContext   # Specific test file

Coverage thresholds are enforced in frontend/jest.config.js (branches 54%, functions 59%, lines 61%, statements 60%) and are raised incrementally as coverage improves.

Test Structure

// Component.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { QuantitySelector } from './QuantitySelector';

describe('QuantitySelector', () => {
  it('increments quantity when plus clicked', () => {
    const onChange = jest.fn();
    render(<QuantitySelector quantity={1} onQuantityChange={onChange} />);

    fireEvent.click(screen.getByLabelText('Verhoog aantal'));

    expect(onChange).toHaveBeenCalledWith(2);
  });

  it('disables decrement at minimum', () => {
    render(<QuantitySelector quantity={1} onQuantityChange={() => {}} min={1} />);

    expect(screen.getByLabelText('Verlaag aantal')).toBeDisabled();
  });
});

Running Specific Tests

# Single test file
npm test TeamOrderContext.test.tsx

# Single test case
npm test -- -t "should save team order to context"

# All tests matching pattern (Jest 30: --testPathPatterns, plural)
npm test -- --testPathPatterns=contexts

E2E Testing (Playwright)

Run Tests

cd frontend
npm run test:e2e            # All E2E tests (headless)
npm run test:e2e:headed     # See browser while testing
npm run test:e2e:ui         # Interactive Playwright UI

Running Specific E2E Tests

# Single test file
npx playwright test team-order-designer-flow.spec.ts

# Single test case
npx playwright test -g "should preserve team order"

# By tag
npx playwright test --grep="@smoke"

# Specific browser
npx playwright test --project=chromium
npx playwright test --project=firefox
npx playwright test --project=webkit

Test Structure

// e2e/checkout.spec.ts
import { test, expect } from '@playwright/test';

test.describe('Checkout Flow', () => {
  test('complete order', async ({ page }) => {
    await page.goto('/products/classic-t-shirt');
    await page.click('[data-testid="color-swatch-1"]');
    await page.click('[data-testid="size-option-3"]');
    await page.click('[data-testid="add-to-cart"]');
    await page.click('[data-testid="checkout-button"]');
    await page.fill('[name="email"]', 'test@example.com');
    await page.click('[data-testid="submit-order"]');
    await expect(page.locator('.order-confirmation')).toBeVisible();
  });
});

Viewing E2E Results

  • HTML Report: Auto-opens after test run, or manually: npx playwright show-report
  • Trace Viewer: For failed tests: npx playwright show-trace trace.zip

VS Code Setup

Extension ID Purpose
Jest Orta.vscode-jest Inline test results, coverage, run/debug tests
Playwright Test ms-playwright.playwright Run/debug E2E tests, record tests, trace viewer

Install via terminal:

code --install-extension Orta.vscode-jest
code --install-extension ms-playwright.playwright

Configure Jest Extension

Add to .vscode/settings.json:

{
  "jest.autoRun": "off",
  "jest.showCoverageOnLoad": false,
  "jest.rootPath": "frontend"
}

VS Code Shortcuts

Unit Tests (Jest):

  • Hover over test and click play button to run
  • Right-click test for "Debug Test"
  • Cmd+Shift+P then "Jest: Toggle Coverage"

E2E Tests (Playwright):

  • Click play button in gutter next to test
  • Right-click for "Debug Test"
  • Cmd+Shift+P then "Test: Record New"

Debugging Tests

Debug Unit Tests (Jest)

  1. Set breakpoint (click left of line number)
  2. Right-click test and select "Debug Test"
  3. Use debug toolbar to step through

node --inspect-brk node_modules/.bin/jest --runInBand TeamOrderContext
Then open chrome://inspect in Chrome.

Debug E2E Tests (Playwright)

npx playwright test --debug
  1. Set breakpoint in test file
  2. Right-click test and select "Debug Test"
  3. Playwright Inspector opens automatically

npm run test:e2e:headed
See browser actions in real-time.

CI/CD Integration

Tests run automatically via GitHub Actions on every push to main/staging and on every PR against those branches. See .github/workflows/ci.yml.

CI Jobs on Every PR / Push

  • backend-test — flake8, migration checks, full pytest suite (coverage ≥60%)
  • frontend-test — TypeScript check, ESLint, full Jest suite (coverage thresholds from jest.config.js)
  • frontend-build — Next.js production build
  • e2e-smoke-test — Playwright @smoke tests
  • e2e-visual-test — Playwright visual regression tests

On pushes to staging, the deploy job runs after all of the above are green and deploys to staging. The seed data export/import round-trip and dependency security scans run only on the weekly schedule or manual workflow_dispatch. Staging→main promotion PRs skip the heavy jobs (the code was already tested on staging); check-base is the only required check on main.

E2E Strategy in CI

Trigger Tests Run Browsers
Pull requests & pushes (main/staging) Smoke tests (@smoke tag) + visual regression Chromium
Weekly schedule or manual dispatch (run_full_e2e) Full E2E suite (all tests) Chromium

Locally, npm run test:e2e runs all configured Playwright projects (chromium, firefox, webkit, mobile, visual).

Pre-push Hook

The pre-push git hook (.githooks/pre-push) runs a backend test subset (requires local PostgreSQL on 5432 and Redis on 6379, e.g. via docker-compose.dev.yml), the TypeScript check, a non-blocking CodeRabbit review, and the Jest suite with coverage. If checks fail, the push is blocked. Never bypass with --no-verify.

Test File Locations

backend/
├── apps/
│   ├── payments/tests/              # Payment tests
│   │   ├── test_models.py           # Payment, Refund, AuditLog model tests
│   │   ├── test_services.py         # PaymentService unit tests
│   │   ├── test_views.py            # API endpoint tests
│   │   ├── test_webhook.py          # Mollie webhook handler tests
│   │   ├── test_refund_service.py   # Refund creation/validation
│   │   ├── test_refund_webhook.py   # Refund webhook processing
│   │   ├── test_retry.py            # Payment retry tests
│   │   └── test_audit.py            # Audit logging tests
│   ├── invoices/tests/              # Invoice tests
│   │   ├── test_invoice_service.py  # Generation, numbering, idempotency
│   │   └── test_invoice_views.py    # Download authorization tests
│   └── orders/tests/
│       └── test_refund_admin.py     # Admin refund action tests

frontend/
├── __tests__/                    # Unit tests
│   ├── contexts/
│   │   └── TeamOrderContext.test.tsx
│   └── README.md
├── e2e/                          # E2E tests
│   ├── flows/
│   │   └── customer-journey.spec.ts  # Full payment journey
│   ├── team-order-designer-flow.spec.ts
│   ├── anonymous-team-order.spec.ts
│   └── ...
├── coverage/                     # Coverage reports (generated)
├── playwright-report/            # E2E reports (generated)
└── test-results/                # E2E artifacts (generated)

Troubleshooting

"Cannot find module '@testing-library/react'"

cd frontend && npm install

"Playwright browsers not installed"

cd frontend && npx playwright install --with-deps

Tests timing out

Increase timeout in test file:

test.setTimeout(60000); // 60 seconds

Port 3000 already in use (E2E tests)

lsof -ti:3000 | xargs kill -9

Coverage not showing

npm test -- --clearCache
npm run test:coverage

Quick Reference

Task Command
Backend
All backend tests cd backend && python manage.py test
Payment tests python manage.py test apps.payments
Invoice tests python manage.py test apps.invoices
Webhook tests python manage.py test apps.payments.tests.test_webhook
Frontend (Jest)
All unit tests npm test
Watch mode npm run test:watch
With coverage npm run test:coverage
Frontend (Playwright)
All E2E tests npm run test:e2e
Headed mode npm run test:e2e:headed
Interactive UI npm run test:e2e:ui
Debug E2E npx playwright test --debug
Smoke tests only npx playwright test --grep="@smoke"
View report npx playwright show-report