Testing Strategy
This page describes how tests are organized in DQM-ML and how to add new tests.
Test Organization
flowchart TB
subgraph "Test Types"
U[Unit Tests\n tests/unit]
I[Integration Tests\n tests/integration]
C[CLI Tests\n tests/cli]
P[Packaging Tests\n scripts/packaging]
B[Benchmark Tests\n tests/benchmark]
end
subgraph "Test Pyramid"
TP[Unit Tests - Fast, isolated\n Core logic, processors]
TI[Integration Tests - Real data\n Pipelines, loaders, metrics]
TC[CLI Tests - End-to-end\n Commands, config files]
PP[Packaging Tests - Isolated venvs\n Package installation scenarios]
end
U --> TP
I --> TI
C --> TC
P --> PP
F[Fixtures - Shared test data\n tests/fixtures/ and tests/integration/fixtures/]
TP --> F
TI --> F
B --> F
B -.-> BN[Compares v1 IRT-SystemX/dqm-ml\n and v2 this repo metric values\n on open-source datasets]
Test Directory Structure
tests/
├── conftest.py # Pytest configuration & fixture imports
├── fixtures/ # Shared cross-test fixtures
│ ├── cli_fixtures.py # CLI environment fixtures
│ ├── stress_images.py # Stress test image generators
│ └── test_fixtures.py # Generic test fixtures (mock data, temp paths)
├── utils/ # Utility functions for tests
│ ├── files.py # File handling helpers
│ ├── jobs.py # Job configuration helpers
│ ├── pipeline_configs.py # Pipeline config builders
│ └── plots.py # Visualization helpers
├── unit/ # Unit tests (fast, isolated)
│ ├── core/ # Core API tests (data_processor, metric_runner)
│ ├── pipeline/ # Pipeline tests (loaders, writers)
│ └── v2/ # CLI wrapper tests
├── integration/ # Integration tests (synthetic data)
│ ├── fixtures/ # Integration-specific fixtures and data
│ │ ├── config.py # Configuration fixtures
│ │ ├── data.py # Data fixtures
│ │ ├── jobs.py # Job configuration fixtures
│ │ └── paths.py # Path fixtures
│ ├── test_completeness.py
│ ├── test_representativeness.py
│ ├── test_domain_gap.py
│ ├── test_visual_features.py
│ └── test_pandas_welding.py
├── benchmark/ # Benchmark tests (record metric values)
│ └── test_benchmark_domain_gap.py
└── cli/ # CLI end-to-end tests
├── test_v2_wrapper.py
└── test_job_cli.py
Test Fixtures
DQM-ML uses pytest fixtures for reusable test data. Here's what's available:
| Fixture | Scope | Purpose | Usage |
|---|---|---|---|
test_path |
session | Tests directory path | All test files |
output_path |
session | Output directory for test results | Integration tests |
coco_data |
session | COCO dataset for domain gap tests | test_domain_gap.py |
normal_dist |
function | Normal distribution sample | Representativeness tests |
not_normal_dist |
function | Non-normal distribution | Statistical tests |
uniform_dist |
function | Uniform distribution | Statistical tests |
not_uniform_dist |
function | Non-uniform distribution | Statistical tests |
job_completeness |
function | Completeness job config | Pipeline tests |
job_representativeness |
function | Representativeness job config | Pipeline tests |
job_domain_gap |
function | Domain gap job config | Pipeline tests |
job_visual_features |
function | Visual features job config | Pipeline tests |
all_classes |
session | All available pipeline classes | CLI tests |
coco_data_dir |
session | Path to COCO data directory | Benchmark tests |
coco_parquet_path |
session | Path to COCO parquet file | Benchmark tests |
mock_parquet_dataset |
function | Small mock Parquet dataset | Unit tests |
sample_dataframe |
function | Small sample DataFrame | Unit tests |
temp_output_path |
function | Temporary output directory | All test types |
Example using fixtures:
import pytest
def test_completeness_with_data(
test_path: str,
uniform_dist: Any
) -> None:
"""Test completeness metric with uniform distribution."""
processor = CompletenessProcessor(
name="test",
config={"columns": {"input": ["feature"]}}
)
result = processor.compute({})
assert result is not None
Running Tests
# All tests with coverage report
uv run nox -s test
# Fast mode (skip slow tests)
uv run pytest -m "not slow"
# Specific test file
uv run pytest tests/integration/test_completeness.py
# With verbose output
uv run pytest -v tests/
# Run only unit tests
uv run pytest tests/unit/
# Run with coverage for specific package
uv run pytest --cov=packages/dqm-ml-core tests/
# Run only packaging isolation tests (shell scripts, not pytest)
scripts/packaging/test_local.sh
Adding a New Test
-
Choose test type:
- Unit tests:
tests/unit/package_name/- fast, isolated tests of classes and functions - Integration tests:
tests/integration/- tests that calldqm-ml processwith YAML configs on synthetic data - CLI tests:
tests/cli/- end-to-end tests of command-line usage - Packaging tests:
scripts/packaging/- shell scripts that verify subsets of packages work independently in isolated venvs (see Packaging Tests) - Benchmark tests:
tests/benchmark/- compute metrics on open-source datasets (COCO) to compare metric values between v1 and v2 (this repo); no pass/fail assertions, records results for manual inspection
- Unit tests:
-
Follow naming conventions:
- Test files:
test_*.py - Test functions:
test_* - Use descriptive names:
test_completeness_returns_valid_score
- Test files:
-
Use existing fixtures:
-
Mark slow tests (if your test takes >30s):
Test Data Sources
| Type | Source | When to Use |
|---|---|---|
| Synthetic | Generated via fixtures (normal_dist, uniform_dist) | Most unit/integration tests |
| Real datasets | COCO-2017 via fiftyone.zoo |
Domain gap tests |
| Example data | examples/config/ |
CLI tests |
Test Coverage & Results
After running tests, reports are generated:
| Report | Location | Description |
|---|---|---|
| Coverage HTML | docs/reports/htmlcov/index.html |
Line-by-line coverage |
| Test Results HTML | docs/reports/pytest/pytest_report.html |
Test execution report |
CI/CD Testing
Tests run automatically on every push via GitHub Actions:
- Lint: Code style with ruff
- Type Check: Type safety with mypy
- Test: Full test suite with pytest
- Docs: Documentation build
See the README for current status badges.