---
title: "Test Management"
description: "Best practices for organizing tests, maximizing coverage, and maintaining test-requirement traceability"
---

# Test Management Best Practices

Effective test management ensures comprehensive coverage, reduces defects, and maintains clear traceability between tests and requirements.

## Test Organization

### Test Hierarchy

Organize tests in a structured hierarchy:

```python
from tracertm import TraceRTMClient

client = TraceRTMClient()

# Test Suite (top level)
suite = client.create_item(
    title="Authentication Test Suite",
    item_type="testsuite",
    description="All tests related to user authentication",
    metadata={
        "test_level": "integration",
        "automation_status": "automated"
    }
)

# Test Cases (individual tests)
test_cases = [
    {
        "title": "Verify valid login credentials",
        "description": "User with correct email/password can log in",
        "test_type": "positive"
    },
    {
        "title": "Verify invalid password rejection",
        "description": "User with wrong password sees error message",
        "test_type": "negative"
    },
    {
        "title": "Verify account lockout after 5 failures",
        "description": "Account locks after 5 consecutive failed attempts",
        "test_type": "security"
    }
]

for tc in test_cases:
    test = client.create_item(
        title=tc["title"],
        item_type="test",
        description=tc["description"],
        metadata={
            "test_type": tc["test_type"],
            "automation": "automated",
            "priority": "high"
        }
    )
    # Link to suite
    client.create_link(test.id, suite.id, "child_of")
```

### Test Categories

Categorize tests for better organization:

```python
TEST_CATEGORIES = {
    "unit": {
        "scope": "Single function/method",
        "speed": "< 100ms",
        "isolation": "Full mocking",
        "coverage_target": "80%"
    },
    "integration": {
        "scope": "Component interaction",
        "speed": "< 5s",
        "isolation": "Partial mocking",
        "coverage_target": "70%"
    },
    "e2e": {
        "scope": "Full user flow",
        "speed": "< 60s",
        "isolation": "None",
        "coverage_target": "Key paths"
    },
    "performance": {
        "scope": "System under load",
        "speed": "Minutes",
        "isolation": "Production-like",
        "coverage_target": "Critical paths"
    },
    "security": {
        "scope": "Vulnerability testing",
        "speed": "Varies",
        "isolation": "Isolated environment",
        "coverage_target": "OWASP Top 10"
    }
}

# Apply category to test
test = client.create_item(
    title="API response time under load",
    item_type="test",
    metadata={
        "category": "performance",
        "scope": "system",
        "estimated_duration": "5m"
    }
)
```

## Test-Requirement Linking

### Link Tests to Requirements

Every test should trace to at least one requirement:

```python
# Get requirement
feature = client.get_item("FEAT-001")

# Create test and link
test = client.create_item(
    title="Verify user login",
    item_type="test",
    description="Test that users can authenticate with valid credentials"
)

# Create traceability link
client.create_link(
    source_id=test.id,
    target_id=feature.id,
    link_type="tests",
    metadata={
        "coverage_type": "functional",
        "coverage_level": "full"
    }
)

# Verify linkage
links = client.get_links(source_id=test.id)
print(f"Test {test.id} verifies: {[l.target_id for l in links]}")
```

### Coverage Levels

Document how thoroughly tests cover requirements:

```python
COVERAGE_LEVELS = {
    "full": "Tests all acceptance criteria",
    "partial": "Tests some acceptance criteria",
    "smoke": "Basic functionality check only",
    "regression": "Guards against known bugs"
}

# Link with coverage metadata
client.create_link(
    source_id="TEST-001",
    target_id="FEAT-001",
    link_type="tests",
    metadata={
        "coverage_level": "partial",
        "criteria_covered": ["AC-1", "AC-2"],
        "criteria_not_covered": ["AC-3"],
        "notes": "AC-3 requires manual testing"
    }
)
```

### Bulk Test Linking

```python
# Link multiple tests to a feature
def link_tests_to_feature(client, feature_id, test_ids):
    """Link multiple tests to a feature with coverage tracking."""
    for test_id in test_ids:
        if not client.link_exists(test_id, feature_id, "tests"):
            client.create_link(test_id, feature_id, "tests")

    # Calculate coverage
    coverage = client.get_coverage(feature_id)
    print(f"Feature {feature_id} coverage: {coverage.percentage}%")
    return coverage

# Usage
link_tests_to_feature(
    client,
    "FEAT-001",
    ["TEST-001", "TEST-002", "TEST-003"]
)
```

## Test Coverage

### Coverage Metrics

Track multiple coverage dimensions:

```python
# Get coverage report
coverage = client.get_coverage(
    project_id="PROJ-001",
    include_types=["feature", "story"]
)

print(f"Overall coverage: {coverage.percentage}%")
print(f"Features covered: {coverage.covered}/{coverage.total}")

# Breakdown by type
for item_type, stats in coverage.by_type.items():
    print(f"  {item_type}: {stats['covered']}/{stats['total']} ({stats['percentage']}%)")

# Identify gaps
print("\nUncovered items:")
for item in coverage.uncovered:
    print(f"  {item.id}: {item.title}")
```

### Coverage Targets

Set and enforce coverage targets:

```yaml
# .tracertm/config.yaml
coverage:
  targets:
    feature:
      minimum: 80
      warn_below: 90
    story:
      minimum: 70
      warn_below: 85

  enforcement:
    block_merge_below: 80
    notify_on_decrease: true

  exclusions:
    - item_type: doc
    - metadata.skip_coverage: true
```

```bash
# Check coverage against targets
tracertm coverage --check --min 80

# Generate coverage report
tracertm report coverage --output coverage.html
```

### Coverage Analysis

```python
def analyze_coverage(client, project_id):
    """Comprehensive coverage analysis."""
    coverage = client.get_coverage(project_id)

    analysis = {
        "summary": {
            "total": coverage.total,
            "covered": coverage.covered,
            "percentage": coverage.percentage
        },
        "by_priority": {},
        "gaps": [],
        "risks": []
    }

    # Analyze by priority
    for item in client.get_items(project_id=project_id, item_type="feature"):
        priority = item.metadata.get("priority", "medium")

        if priority not in analysis["by_priority"]:
            analysis["by_priority"][priority] = {"total": 0, "covered": 0}

        analysis["by_priority"][priority]["total"] += 1

        item_coverage = client.get_coverage(item.id)
        if item_coverage.percentage > 0:
            analysis["by_priority"][priority]["covered"] += 1
        else:
            analysis["gaps"].append({
                "id": item.id,
                "title": item.title,
                "priority": priority
            })

    # Identify high-priority gaps as risks
    analysis["risks"] = [
        g for g in analysis["gaps"]
        if g["priority"] in ["critical", "high"]
    ]

    return analysis

# Run analysis
analysis = analyze_coverage(client, "PROJ-001")
print(f"High-priority gaps: {len(analysis['risks'])}")
```

## Test Execution

### Recording Test Results

```python
# Record test execution
def record_test_result(client, test_id, result):
    """Record test execution result."""
    client.update_item(
        test_id,
        metadata={
            "last_execution": {
                "timestamp": datetime.now().isoformat(),
                "status": result["status"],  # passed, failed, skipped
                "duration": result["duration"],
                "environment": result["environment"],
                "build": result["build"],
                "error_message": result.get("error")
            },
            "execution_history": client.get_item(test_id).metadata.get(
                "execution_history", []
            )[-9:] + [result]  # Keep last 10
        }
    )

    return client.get_item(test_id)

# Usage
record_test_result(client, "TEST-001", {
    "status": "passed",
    "duration": 1.5,
    "environment": "staging",
    "build": "v1.2.3"
})
```

### Test Run Management

```python
# Create test run
test_run = client.create_item(
    title=f"Release v1.2.3 Test Run",
    item_type="testrun",
    description="Full regression test suite for release v1.2.3",
    metadata={
        "release": "v1.2.3",
        "environment": "staging",
        "started_at": datetime.now().isoformat(),
        "status": "in_progress"
    }
)

# Link tests to run
for test in client.get_items(item_type="test", suite="regression"):
    client.create_link(test.id, test_run.id, "included_in")

# Complete test run
client.update_item(
    test_run.id,
    metadata={
        "completed_at": datetime.now().isoformat(),
        "status": "completed",
        "results": {
            "total": 100,
            "passed": 95,
            "failed": 3,
            "skipped": 2
        }
    }
)
```

### CI/CD Integration

```yaml
# GitHub Actions example
name: Test and Trace

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run tests
        run: pytest --junitxml=results.xml

      - name: Upload results to TraceRTM
        run: |
          tracertm test-results import results.xml \
            --format junit \
            --build ${{ github.sha }} \
            --environment ci

      - name: Check coverage
        run: |
          tracertm coverage --min 80 --fail-below
```

## Test Types

### Functional Tests

```python
# Create functional test
func_test = client.create_item(
    title="Verify checkout flow",
    item_type="test",
    description="""
    Steps:
    1. Add item to cart
    2. Go to checkout
    3. Enter shipping details
    4. Enter payment details
    5. Confirm order

    Expected:
    - Order confirmation displayed
    - Confirmation email sent
    - Inventory updated
    """,
    metadata={
        "test_type": "functional",
        "category": "e2e",
        "automation": "automated",
        "framework": "playwright"
    }
)

# Link to feature
client.create_link(func_test.id, "FEAT-CHECKOUT", "tests")
```

### Negative Tests

```python
# Create negative test
neg_test = client.create_item(
    title="Verify invalid credit card rejection",
    item_type="test",
    description="""
    Steps:
    1. Go to checkout
    2. Enter invalid credit card number
    3. Submit payment

    Expected:
    - Error message displayed
    - Order not created
    - No charge attempted
    """,
    metadata={
        "test_type": "negative",
        "error_handling": True
    }
)
```

### Edge Case Tests

```python
# Create edge case test
edge_test = client.create_item(
    title="Verify cart with 100 items",
    item_type="test",
    description="""
    Steps:
    1. Add 100 different items to cart
    2. View cart
    3. Proceed to checkout

    Expected:
    - Cart displays all items
    - Page loads within 3 seconds
    - Total calculated correctly
    """,
    metadata={
        "test_type": "edge_case",
        "boundary_condition": "max_items"
    }
)
```

### Performance Tests

```python
# Create performance test
perf_test = client.create_item(
    title="Load test: 1000 concurrent checkouts",
    item_type="test",
    description="""
    Scenario:
    - 1000 concurrent users checking out
    - Each user completes full checkout flow
    - 5-minute sustained load

    Acceptance Criteria:
    - 95th percentile response < 2 seconds
    - Error rate < 1%
    - No system crashes
    """,
    metadata={
        "test_type": "performance",
        "subtype": "load",
        "tool": "k6",
        "target_rps": 1000
    }
)
```

## Test Maintenance

### Identify Stale Tests

```python
def find_stale_tests(client, days=90):
    """Find tests not executed recently."""
    stale = []
    cutoff = datetime.now() - timedelta(days=days)

    for test in client.get_items(item_type="test"):
        last_exec = test.metadata.get("last_execution", {})
        if last_exec:
            exec_date = datetime.fromisoformat(last_exec["timestamp"])
            if exec_date < cutoff:
                stale.append({
                    "id": test.id,
                    "title": test.title,
                    "last_executed": exec_date
                })
        else:
            stale.append({
                "id": test.id,
                "title": test.title,
                "last_executed": None
            })

    return stale

# Find stale tests
stale_tests = find_stale_tests(client, days=90)
print(f"Found {len(stale_tests)} stale tests")
```

### Orphan Test Detection

```python
def find_orphan_tests(client):
    """Find tests not linked to any requirement."""
    orphans = []

    for test in client.get_items(item_type="test"):
        links = client.get_links(
            source_id=test.id,
            link_types=["tests", "verifies"]
        )

        if not links:
            orphans.append({
                "id": test.id,
                "title": test.title
            })

    return orphans

# Find orphans
orphans = find_orphan_tests(client)
print(f"Orphan tests: {len(orphans)}")
for orphan in orphans:
    print(f"  {orphan['id']}: {orphan['title']}")
```

### Test Quality Metrics

```python
def calculate_test_metrics(client, project_id):
    """Calculate test quality metrics."""
    tests = client.get_items(project_id=project_id, item_type="test")

    metrics = {
        "total_tests": len(tests),
        "automated": 0,
        "manual": 0,
        "with_coverage_link": 0,
        "executed_last_30_days": 0,
        "pass_rate": 0
    }

    passed = 0
    executed = 0

    for test in tests:
        # Automation status
        if test.metadata.get("automation") == "automated":
            metrics["automated"] += 1
        else:
            metrics["manual"] += 1

        # Coverage links
        links = client.get_links(source_id=test.id, link_types=["tests"])
        if links:
            metrics["with_coverage_link"] += 1

        # Recent execution
        last_exec = test.metadata.get("last_execution", {})
        if last_exec:
            exec_date = datetime.fromisoformat(last_exec["timestamp"])
            if exec_date > datetime.now() - timedelta(days=30):
                metrics["executed_last_30_days"] += 1
                executed += 1
                if last_exec["status"] == "passed":
                    passed += 1

    if executed > 0:
        metrics["pass_rate"] = round(passed / executed * 100, 1)

    return metrics

# Get metrics
metrics = calculate_test_metrics(client, "PROJ-001")
print(f"Automation rate: {metrics['automated']/metrics['total_tests']*100:.1f}%")
print(f"Coverage link rate: {metrics['with_coverage_link']/metrics['total_tests']*100:.1f}%")
print(f"Pass rate (30 days): {metrics['pass_rate']}%")
```

## CLI Commands

```bash
# List all tests
tracertm list --type test

# Show test coverage
tracertm coverage --type feature --verbose

# Find orphan tests
tracertm validate --check orphans --type test

# Import test results
tracertm test-results import results.xml --format junit

# Generate test report
tracertm report tests --output test-report.html

# Find stale tests
tracertm list --type test --not-executed-since "90 days ago"
```

## Related Topics

- [Requirement Writing](/docs/wiki/best-practices/requirement-writing) - Clear requirements
- [Traceability](/docs/wiki/best-practices/traceability) - Linking best practices
- [Artifacts - Test Cases](/docs/wiki/concepts/artifacts/test-cases) - Test artifact details
- [Reports](/docs/getting-started/first-project/reports) - Coverage reports

