annator-command-center / tests /docs /TEST_INFRA_STANDARDS.md
techprotrade's picture
Deploy ATOM FastAPI command center runtime (part 7)
cc036ff verified
|
Raw
History Blame Contribute Delete
10.8 kB
# Test Infrastructure Standards
**Purpose**: Comprehensive guide for test infrastructure standards and patterns in Atom.
**Last Updated**: 2026-03-23
---
## Overview
This document outlines the test infrastructure standards for the Atom project, covering:
- Test fixtures and patterns
- Database isolation and test data management
- Failure artifact capture (screenshots, videos)
- Test reporting (Allure, HTML, JSON)
- Parallel execution patterns
- Test cleanup and teardown
**Target Audience**: Test engineers, developers writing tests, CI/CD engineers
---
## Test Fixtures Standards
### Fixture Scope
**Session-scoped fixtures** (expensive, shared):
```python
@pytest.fixture(scope="session")
def browser():
"""Shared browser instance for all tests."""
# Use for: Browser launch, server startup
```
**Function-scoped fixtures** (default, isolated):
```python
@pytest.fixture(scope="function")
def page(browser):
"""New page for each test."""
# Use for: Page objects, test data
```
**Autouse fixtures** (automatic):
```python
@pytest.fixture(autouse=True)
def track_page_for_screenshots(request, page):
"""Track page for automatic screenshot capture."""
# Runs for every test automatically
```
### Fixture Naming Conventions
- Descriptive names: `screenshot_page`, not `sp`
- Return type hints in docstrings
- Yield for cleanup, return for values
---
## Database Isolation Patterns
### Worker-Specific Database Isolation
All tests automatically get worker-specific database isolation via `db_session` fixture:
```python
@pytest.fixture(scope="function")
def db_session():
"""
Worker-specific database session with automatic rollback.
Each pytest-xdist worker gets its own database file to prevent
test data collisions during parallel execution.
"""
# Worker ID: gw0, gw1, gw2, etc.
# Database: atom_test_<worker_id>.db
# Automatic rollback after each test
```
**Key Benefits**:
- No test data collisions between workers
- No cleanup code needed in tests
- Full parallel execution support
**Usage**:
```python
def test_create_agent(db_session):
agent = AgentRegistry(id="test-agent", name="Test")
db_session.add(agent)
db_session.commit()
# Auto-rollback after test
```
---
## Failure Artifacts (INFRA-04)
### Automatic Capture
Failed E2E tests automatically capture:
- **Screenshot**: Full-page screenshot saved to `artifacts/screenshots/`
- **Video**: Browser recording saved to `artifacts/videos/` (CI only)
- **Allure Report**: Artifacts attached to test result for easy viewing
### Screenshot Format
- Filename: `{timestamp}_{test_name}.png`
- Full page capture (includes scrolled content)
- Available in: `artifacts/screenshots/`
**Example**:
```
20260323_170120_test_login_success.png
```
### Video Recording
- Enabled in CI only (GITHUB_ACTIONS=true)
- Format: WEBM
- Available in: `artifacts/videos/`
- Accessed via: `page.video.path()`
**Example**:
```
20260323_170120_test_login_success.webm
```
### Allure Integration
```python
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
"""Attach screenshots/videos to Allure on failure."""
outcome = yield
rep = outcome.get_result()
if rep.when == "call" and rep.failed:
# Capture and attach artifacts
allure.attach.file(screenshot_path, ...)
allure.attach.file(video_path, ...)
```
**Benefits**:
- Artifacts embedded in test report
- One-click viewing in Allure UI
- No manual file searching
### Viewing Artifacts
```bash
# Local: Check artifacts directory
ls artifacts/screenshots/
ls artifacts/videos/
# CI: Download from workflow run artifacts
# Allure: Open report and click failed test
allure open allure-report
```
### Manual Screenshot Capture
```python
def test_manual_screenshot(page):
page.screenshot(path="custom-screenshot.png")
# Attach to Allure
allure.attach.file(
"custom-screenshot.png",
name="Custom Screenshot",
attachment_type=allure.attachment_type.PNG
)
```
---
## Test Cleanup Patterns
### Automatic Cleanup
Use fixtures with yield for cleanup:
```python
@pytest.fixture(scope="function")
def temp_file():
"""Create temp file and clean up after test."""
path = "/tmp/test-file.txt"
with open(path, "w") as f:
f.write("test data")
yield path
# Cleanup
os.remove(path)
```
### Database Cleanup
Use transaction rollback (automatic):
```python
def test_create_agent(db_session):
agent = AgentRegistry(id="test", name="Test")
db_session.add(agent)
db_session.commit()
# Auto-rollback after test
```
### Resource Cleanup
```python
@pytest.fixture(scope="function")
def server():
"""Start server and stop after test."""
srv = start_server(port=8000)
yield srv
srv.stop() # Cleanup
```
---
## Test Reporting Standards
### Allure Reporting
**Generate Report**:
```bash
pytest tests/ --alluredir=allure-results
allure generate allure-results --clean -o allure-report
allure open allure-report
```
**Benefits**:
- Rich test report with screenshots/videos
- Test history and trends
- Suites, categories, and tags
### HTML Reporting
**Generate Report**:
```bash
pytest tests/ --html=report.html --self-contained-html
```
**Features**:
- Embedded screenshots in failed tests
- Test execution summary
- Test duration and logs
### JSON Reporting
**Generate Report**:
```bash
pytest tests/ --json-report --json-report-file=report.json
```
**Use Case**: CI/CD pass rate parsing
---
## Parallel Execution Standards
### pytest-xdist Configuration
**Run tests in parallel**:
```bash
pytest -n auto # Auto-detect CPU count
pytest -n 4 # Use 4 workers
```
**Worker Isolation**:
- Each worker gets own database file
- Unique resource names via fixtures
- No shared state between workers
### Test Independence
**Requirements**:
- No hardcoded IDs (use fixtures)
- No shared files (use temp files)
- No hardcoded ports (use random ports)
**Bad Example**:
```python
def test_create_agent():
agent = AgentRegistry(id="test-agent", ...) # Collision!
```
**Good Example**:
```python
def test_create_agent(unique_name):
agent = AgentRegistry(id=unique_name(), ...) # Safe
```
---
## CI/CD Integration
### CI-Specific Behavior
**Video Recording**: Enabled in CI only
```python
if is_ci_environment():
context_args["record_video_dir"] = video_dir
```
**Test Retries**: Enabled in CI only
```python
if is_ci_environment():
sys.argv.extend(["--reruns", "2"])
```
**Artifact Upload**: Screenshots and videos uploaded as workflow artifacts
### GitHub Actions Example
```yaml
- name: Run E2E tests
run: pytest tests/e2e_ui/ -n auto
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v3
with:
name: screenshots
path: backend/tests/e2e_ui/artifacts/screenshots/
```
---
## Best Practices
### DO ✅
- Use fixtures for setup/teardown
- Write isolated tests (no dependencies)
- Use unique resource names
- Leverage automatic rollback
- Attach screenshots to Allure on failure
### DON'T ❌
- Hardcode IDs or ports
- Share state between tests
- Manually cleanup database
- Skip fixture cleanup
- Ignore flaky tests (fix them)
---
## Troubleshooting
### Screenshots Not Capturing
**Check**:
1. Is `page` fixture available?
2. Is `allure-pytest` installed?
3. Is `allure-results` directory writable?
**Fix**:
```bash
pip install allure-pytest
mkdir -p allure-results
```
### Videos Not Recording
**Check**:
1. Is running in CI (GITHUB_ACTIONS=true)?
2. Is video dir created?
3. Is browser context configured for video?
**Fix**:
```python
if is_ci_environment():
os.makedirs(video_dir, exist_ok=True)
context_args["record_video_dir"] = video_dir
```
### Tests Failing in Parallel but Passing Sequentially
**Check**:
1. Are resources uniquely named?
2. Is database isolation enabled?
3. Are ports hardcoded?
**Fix**:
```python
# Use unique_name fixture
agent = AgentRegistry(id=unique_name(), ...)
# Use db_session fixture (auto-isolation)
def test_something(db_session):
...
```
---
## Additional Resources
- **Test Isolation Patterns**: `TEST_ISOLATION_PATTERNS.md`
- **Flaky Test Guide**: `FLAKY_TEST_GUIDE.md`
- **Parallel Execution Guide**: `PARALLEL_EXECUTION_GUIDE.md`
- **Coverage Guide**: `COVERAGE_GUIDE.md`
## Unified Test Runner (INFRA-08, INFRA-09, INFRA-10)
### Overview
Single entry point for running all platform tests with unified Allure reporting.
### Usage
```bash
# Run all platforms
python backend/tests/scripts/test_runner.py
# Run specific platform
python backend/tests/scripts/test_runner.py --platform backend
python backend/tests/scripts/test_runner.py --platform web
python backend/tests/scripts/test_runner.py --platform mobile
# Configure parallelism
python backend/tests/scripts/test_runner.py --workers 8
# Skip Allure report (faster iteration)
python backend/tests/scripts/test_runner.py --no-report
# Extra pytest arguments
python backend/tests/scripts/test_runner.py --extra "-k test_agent"
```
### Platforms
| Platform | Test Framework | Allure Directory |
|----------|---------------|------------------|
| Backend | pytest | allure-results/backend/ |
| Web E2E | pytest-playwright | allure-results/web/ |
| Mobile API | pytest | allure-results/mobile/ |
| Desktop | cargo test | (separate workflow) |
### Allure Report
```bash
# Generate report manually
python backend/tests/scripts/allure_aggregator.py aggregate-allure \
--backend allure-results/backend/ \
--web allure-results/web/ \
--mobile allure-results/mobile/ \
--output allure-results/
# View report (auto-refreshes)
allure open allure-report
# Serve report on port 8080
allure serve allure-results
```
### CI Integration
- GitHub Actions runs unified-tests job per platform
- generate-report job aggregates results
- Report uploaded as artifact for viewing
### Output
- Allure results: `allure-results/`
- Allure report: `allure-report/index.html`
- Screenshots: `artifacts/screenshots/` (on failure)
- Videos: `artifacts/videos/` (on failure, CI only)
---
*Last Updated: 2026-03-23*
*Phase: 233 - Test Infrastructure Foundation*
*Plan: 05 - Unified Test Runner with Allure Cross-Platform Reporting*