Running Tests#
This guide covers how to run FairDM tests locally using pytest, including filtering, reporting, and debugging options.
Prerequisites#
Ensure you have installed FairDM’s development dependencies:
poetry install --with dev
This installs:
pytest: Test runner
pytest-django: Django integration for database, fixtures, and settings
pytest-cov: Coverage.py integration
factory-boy: Test data factories
Basic Test Execution#
Run All Tests#
poetry run pytest
This executes all tests in tests/ with default settings.
Run Tests by Layer#
# Unit tests only (no database)
poetry run pytest tests/unit/
# Integration tests only (database required)
poetry run pytest tests/integration/
# Contract tests only (API validation)
poetry run pytest tests/contract/
See Test Layers for layer definitions.
Run Tests by Module#
# Single test module
poetry run pytest tests/unit/fairdm/core/test_models.py
# All tests in an app
poetry run pytest tests/integration/fairdm/core/
# Specific test function
poetry run pytest tests/unit/fairdm/core/test_models.py::test_project_creation__with_valid_data__creates_project
Using Test Markers#
FairDM uses pytest markers to categorize tests. See Test Organization for all markers.
Run by Marker#
# Unit tests only (no database access)
poetry run pytest -m unit
# Integration tests only (database required)
poetry run pytest -m integration
# Contract tests only (API validation)
poetry run pytest -m contract
# Slow tests only (>1 second)
poetry run pytest -m slow
# Skip slow tests
poetry run pytest -m "not slow"
Combine Markers#
# Unit tests that are not slow
poetry run pytest -m "unit and not slow"
# Integration or contract tests
poetry run pytest -m "integration or contract"
Verbose Output#
Basic Verbose Mode#
# Show test names as they run
poetry run pytest -v
# Show even more detail (test docstrings, fixtures)
poetry run pytest -vv
Example output:
tests/unit/fairdm/core/test_models.py::test_project_creation__with_valid_data__creates_project PASSED
tests/unit/fairdm/core/test_models.py::test_project_validation__missing_title__raises_error PASSED
Show Print Statements#
# Show print() and logging output from passing tests
poetry run pytest -s
# Combine with verbose mode
poetry run pytest -vv -s
Use -s when debugging test failures to see intermediate values.
Show Captured Output on Failure#
By default, pytest captures stdout and stderr and only shows them for failing tests:
# Default behavior (captured output shown on failure)
poetry run pytest
# Disable capture entirely (show all output immediately)
poetry run pytest --capture=no
Database Management#
Transaction Rollback (Default)#
By default, pytest-django wraps each test in a transaction and rolls it back:
poetry run pytest
Fast: No database cleanup overhead
Isolated: Each test starts with clean state
Limitation: Cannot test transaction commit behavior
See Database Strategy for details.
Reuse Database#
Speed up test runs by reusing the test database across sessions:
# Create database once, reuse on subsequent runs
poetry run pytest --reuse-db
When to use:
Frequent local testing: Avoid database creation overhead
Stable migrations: Database schema hasn’t changed
When to recreate:
After adding/modifying migrations
After changing database settings
Recreate database explicitly:
# Force database recreation
poetry run pytest --create-db
Parallel Testing#
Run tests in parallel using pytest-xdist (not installed by default):
# Install pytest-xdist
poetry add --group dev pytest-xdist
# Run tests in parallel (auto-detect CPU count)
poetry run pytest -n auto
# Run with specific worker count
poetry run pytest -n 4
Note: Each worker gets its own database (test_fairdm_gw0, test_fairdm_gw1, etc.).
Test Selection Strategies#
Run Last Failed Tests#
# Re-run only tests that failed in the last run
poetry run pytest --lf
# Run failed tests first, then all others
poetry run pytest --ff
Useful for debugging failures without re-running the entire suite.
Run Tests Matching Keyword#
# Run tests with "project" in the name
poetry run pytest -k project
# Run tests with "validation" but not "slow"
poetry run pytest -k "validation and not slow"
Example:
# Matches: test_project_creation, test_project_validation
# Skips: test_dataset_creation
poetry run pytest -k project
Stop on First Failure#
# Stop immediately on first failure
poetry run pytest -x
# Stop after 3 failures
poetry run pytest --maxfail=3
Useful for fixing errors incrementally.
Coverage Reporting#
Run tests with coverage tracking:
# Terminal report with missing line numbers
poetry run pytest --cov=fairdm --cov-report=term-missing
# HTML report (open htmlcov/index.html)
poetry run pytest --cov=fairdm --cov-report=html
# XML report (for CI/CD)
poetry run pytest --cov=fairdm --cov-report=xml
See Coverage for detailed coverage guide.
Coverage by Test Layer#
# Unit test coverage
poetry run pytest tests/unit/ --cov=fairdm --cov-report=term-missing
# Integration test coverage
poetry run pytest tests/integration/ --cov=fairdm --cov-report=term-missing
This shows which test layers cover which code paths.
Common Workflows#
Quick Feedback Loop#
# Fast: Unit tests only, no coverage, stop on first failure
poetry run pytest tests/unit/ -x --ff
Pre-Commit Checks#
# Comprehensive: All tests with coverage
poetry run pytest --cov=fairdm --cov-report=term-missing
Debugging Failed Test#
# Verbose, show prints, stop on first failure, show locals
poetry run pytest -vv -s -x --showlocals
# Use Python debugger (pdb) on failure
poetry run pytest --pdb
Using --showlocals: Shows local variable values when tests fail.
Using --pdb: Drops into Python debugger on failure (type help for commands).
CI/CD Simulation#
# Recreate database, run all tests, generate XML coverage
poetry run pytest --create-db --cov=fairdm --cov-report=xml --cov-report=term
Configuration#
FairDM’s pytest settings are in pyproject.toml:
[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "tests.settings"
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
testpaths = ["tests"]
markers = [
"unit: Unit tests (isolated logic, no database)",
"integration: Integration tests (database, file system)",
"contract: Contract tests (API validation, external contracts)",
"slow: Tests that take >1 second to run",
]
addopts = [
"--strict-markers",
"--tb=short",
"--disable-warnings",
]
Key settings:
DJANGO_SETTINGS_MODULE: Test-specific Django settingstestpaths: Only discover tests intests/directory--strict-markers: Prevent typos in marker names--tb=short: Concise traceback format
Troubleshooting#
Tests Can’t Import FairDM Modules#
Symptom: ModuleNotFoundError: No module named 'fairdm'
Cause: FairDM not installed in Poetry environment.
Solution:
poetry install
Database Permission Errors#
Symptom: django.db.utils.OperationalError: permission denied to create database
Cause: Test database user lacks CREATEDB privilege.
Solution:
# Grant CREATEDB to test user (PostgreSQL)
ALTER USER fairdm_test CREATEDB;
Or configure tests/settings.py to use SQLite:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:',
}
}
Migrations Not Applied#
Symptom: django.db.utils.ProgrammingError: relation "fairdm_project" does not exist
Cause: Test database missing migrations.
Solution:
# Recreate test database with migrations
poetry run pytest --create-db
Slow Test Runs#
Symptoms:
Tests take >10 seconds even for small suites
Database creation happens every run
Solutions:
# Reuse database across test runs
poetry run pytest --reuse-db
# Run unit tests only (no database)
poetry run pytest tests/unit/
# Run in parallel
poetry add --group dev pytest-xdist
poetry run pytest -n auto
Confusing Test Output#
Symptom: Can’t tell which test is running or why it failed.
Solutions:
# Show test names as they run
poetry run pytest -v
# Show captured output (prints, logs)
poetry run pytest -s
# Show local variables on failure
poetry run pytest --showlocals
Best Practices#
✅ DO#
Run unit tests frequently:
# Fast feedback loop (2-5 seconds)
poetry run pytest tests/unit/ -x
Use --reuse-db for local development:
# Saves 2-3 seconds per run
poetry run pytest --reuse-db
Run full suite before committing:
# Comprehensive validation
poetry run pytest --cov=fairdm --cov-report=term-missing
Use markers to focus on relevant tests:
# Working on API? Test contracts only
poetry run pytest -m contract
❌ DON’T#
Don’t skip database recreation after migrations:
# Bad: Outdated schema causes cryptic errors
poetry run pytest --reuse-db # After adding migration
# Good: Recreate database
poetry run pytest --create-db
Don’t run integration tests in the inner loop:
# Bad: Slow feedback (10-30 seconds)
poetry run pytest tests/integration/ -x
# Good: Fast unit tests first (2-5 seconds)
poetry run pytest tests/unit/ -x
Don’t ignore test warnings:
# Bad: Warnings hidden, bugs lurk
poetry run pytest --disable-warnings
# Good: Fix warnings incrementally
poetry run pytest # Shows warnings
Further Reading#
See also
Test Layers - Understand unit, integration, and contract test types
Test Organization - Directory structure and naming conventions
Coverage - Measure and interpret test coverage
Database Strategy - Transaction management and test isolation
pytest Documentation - Official pytest documentation
Examples#
Example: Debug Failing Integration Test#
# 1. Run only the failing test with verbose output
poetry run pytest tests/integration/fairdm/core/test_api.py::test_create_project -vv -s
# 2. Show local variables on failure
poetry run pytest tests/integration/fairdm/core/test_api.py::test_create_project --showlocals
# 3. Drop into debugger on failure
poetry run pytest tests/integration/fairdm/core/test_api.py::test_create_project --pdb
Example: Pre-Commit Validation#
# Run all tests with coverage, stop on first failure
poetry run pytest --cov=fairdm --cov-report=term-missing -x
# If all pass, check HTML coverage for gaps
poetry run pytest --cov=fairdm --cov-report=html
# Open htmlcov/index.html
Example: Performance Testing#
# Find slow tests (>1 second)
poetry run pytest --durations=10
# Mark slow tests and skip them
poetry run pytest -m "not slow"
Example: Contract Validation#
# Run only contract tests with verbose output
poetry run pytest -m contract -vv
# Verify API responses match OpenAPI spec
poetry run pytest tests/contract/test_api_contract.py -vv