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¶
Recommended Extensions¶
| 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:
Configure Jest Extension¶
Add to .vscode/settings.json:
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)¶
Debug E2E Tests (Playwright)¶
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
@smoketests - 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'"¶
"Playwright browsers not installed"¶
Tests timing out¶
Increase timeout in test file:
Port 3000 already in use (E2E tests)¶
Coverage not showing¶
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 |