crefft Source 2026.10.4

Testing Guide

crefft > Process > Testing

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

Cross-cutting testing concepts. Language-specific implementations are in each language guide.

Core Principle: Local-First Testing

All tests MUST run locally without external dependencies.

  • Unit tests: MUST NOT use network, Docker, or databases
  • Integration tests: MUST use testcontainers or local services
  • Tests MUST NOT require cloud accounts or remote APIs
  • External services MUST be mocked; test data MUST use fixtures
  • CI MUST reproduce local test environment exactly

Testing Pyramid

The testing pyramid1 is a framework for balancing test types:

          /\
         /  \           E2E (few, slow, expensive)
        /----\
       /      \         Acceptance (user journeys)
      /--------\
     /          \       Integration (component interactions)
    /------------\
   /              \     Unit (many, fast, cheap)
  /----------------\

Test Types

Unit Tests

Test individual functions/methods in isolation2.

  • MUST be fast: Milliseconds per test
  • MUST be isolated: No external dependencies, no I/O
  • MUST be repeatable: Same result every time
  • MUST be self-validating: Pass or fail, no manual checking

Integration Tests

Test component interactions with real dependencies3.

  • Database operations with testcontainers4
  • API endpoint testing with test clients
  • Message queue interactions
  • Cache layer behavior

E2E Tests

Test complete user flows through the entire system5.

  • Browser automation (Playwright6, Cypress7)
  • Full stack deployed locally
  • Happy path coverage
  • Critical business flows only

Acceptance Tests

Verify business requirements using BDD style8.

  • Given/When/Then format9
  • Written in domain language
  • Cucumber10, SpecFlow11, pytest-bdd12
  • Living documentation

Performance Tests

Measure and validate performance characteristics13.

TypePurposeTools
BenchmarkMeasure function speedpytest-benchmark14, criterion15, testing.B16
LoadTest under expected loadk617, locust18, Gatling19
StressFind breaking pointsk617, Artillery20
SoakDetect memory leaksExtended load tests

Thread Safety Tests

Verify concurrent access is safe21.

  • Race condition detection (Go’s -race22, TSAN23)
  • Deadlock detection
  • Concurrent access patterns
  • Lock contention testing

Idempotence Tests

Verify operations can be safely retried24.

  • Same result on repeated calls
  • No duplicate side effects
  • Database upsert patterns
  • API idempotency keys

Reliability & Resilience Tests

Verify behavior under failure conditions25.

  • Chaos engineering26: Random failure injection
  • Fault injection: Network delays, timeouts, errors
  • Circuit breaker testing27: Verify fallback behavior
  • Retry testing: Backoff and recovery

Compatibility Tests

Verify across environments and versions28.

  • Language version matrix (Python 3.12, 3.13, 3.14)
  • OS matrix (Linux, macOS, Windows)
  • Browser matrix (Chrome, Firefox, Safari)
  • API version compatibility
  • Database version compatibility

Internationalization Tests

Verify i18n/l10n correctness29.

  • UTF-8 handling throughout
  • Unicode edge cases (emoji, RTL, combining chars)
  • Locale-specific formatting (dates, numbers, currency)
  • Translation string coverage
  • Timezone handling

Data Integrity Tests

Verify data correctness and consistency30.

  • Database constraint enforcement
  • Migration up/down testing
  • Referential integrity
  • Backup/restore verification
  • Transaction isolation

A/B Testing & Feature Flags

Test feature flag behavior31.

  • Both flag states (on/off)
  • Gradual rollout percentages
  • User segment targeting
  • Flag cleanup tracking

Test Organization

Directory Structure

tests/
├── unit/              # Fast, isolated tests
├── integration/       # Component interaction tests
├── e2e/               # End-to-end browser tests
├── acceptance/        # BDD feature tests
├── performance/       # Benchmarks, load tests
├── fixtures/          # Shared test data
└── conftest.py        # Shared fixtures (Python)

Test Markers/Tags

Use markers to categorize and selectively run tests:

# Python
pytest -m unit
pytest -m "not slow"

# Go
go test -run TestUnit
go test -short

# JavaScript (Vitest): filter by test name
vitest run -t "unit"

# JavaScript (Vitest): filter by named project
vitest run --project unit

Vitest has no marker system and rejects --grep, so a category filter needs a naming or configuration convention. You MUST choose one of these:

  • Encode the category in the test name and filter with -t (--testNamePattern), which matches the full name as a regular expression.
  • Declare named projects in vitest.config.ts and filter with --project, which binds the category to a file glob and leaves the test name free to describe behavior.
// vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    projects: [
      { test: { name: 'unit', include: ['tests/unit/**/*.test.ts'] } },
      { test: { name: 'integration', include: ['tests/integration/**/*.test.ts'] } },
    ],
  },
});

CI Strategy

Every job MUST select a runner, check out the repository, install a pinned runtime, and install locked dependencies before it runs a test command.

name: Tests

on:
  push:
    branches: [main]
  pull_request:

jobs:
  # Fast feedback first
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
        with:
          python-version: "3.14"
      - run: uv sync --locked
      - run: uv run ruff check .

  unit:
    needs: lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
        with:
          python-version: "3.14"
      - run: uv sync --locked
      - run: uv run pytest -m unit -n auto

  integration:
    needs: unit
    runs-on: ubuntu-latest
    services:
      postgres:
        # postgres:16; registry manifest digest.
        image: postgres@sha256:ca0bd484cb98bf4b24eb1010e73fb3fcbd6714d240fbc1a10eea5b7dbecb641d
        env:
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: test
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
        with:
          python-version: "3.14"
      - run: uv sync --locked
      - run: uv run pytest -m integration
        env:
          DATABASE_URL: postgres://postgres:postgres@localhost:5432/test

  e2e:
    needs: integration
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
        with:
          python-version: "3.14"
      - run: uv sync --locked
      - run: uv run playwright install --with-deps chromium
      - run: uv run pytest tests/e2e/ --tracing retain-on-failure --output test-results
      - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        if: failure()
        with:
          name: e2e-traces
          path: test-results/

  # Slow tests on main only
  performance:
    if: github.ref == 'refs/heads/main'
    needs: e2e
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
        with:
          python-version: "3.14"
      - run: uv sync --locked
      - run: uv run pytest tests/performance/

The postgres service MUST set POSTGRES_PASSWORD: the official image refuses to initialize without it, so the job has no database to reach. The service MUST declare a health check, which is what makes the runner wait until PostgreSQL accepts connections instead of merely until the container starts. Because these jobs run directly on the runner rather than in a job container, the service MUST map 5432:5432 and clients MUST connect via localhost. The e2e job uploads Playwright traces on failure so a red run is diagnosable without a rerun.

Coverage Goals

TypeTargetNotes
Unit80%+Focus on business logic
IntegrationCritical pathsDatabase, APIs
E2EHappy pathsDon’t over-invest
RegressionAll fixed bugsPrevent recurrence

Coverage is a metric, not a goal.32 Focus on testing behavior, not hitting arbitrary numbers. 100% coverage doesn’t mean 100% correct.

Flaky Test Policy

Flaky tests33 undermine confidence. Handle them aggressively:

  1. MUST quarantine immediately: Move to separate suite
  2. MUST fix within 48 hours or delete
  3. MUST NOT ignore: A flaky test is worse than no test
  4. SHOULD track patterns: Network? Timing? Order-dependent?

Test Data Management

  • Fixtures34: Static data files for deterministic tests
  • Factories35: Generate test data programmatically
  • Builders36: Fluent API for complex objects
  • Fakers37: Random but realistic data

You SHOULD prefer factories over fixtures for flexibility. You SHOULD use fixtures for complex scenarios that need exact reproduction.

See Also

References

Footnotes

  1. Testing Pyramid - Cohn, M. (2009). Succeeding with Agile: Software Development Using Scrum. Addison-Wesley. Also see Martin Fowler’s Test Pyramid ↩

  2. Unit Testing - Unit Testing - Martin Fowler ↩

  3. Integration Testing - Integration Test - Martin Fowler - Narrow versus broad integration scope, real dependencies, and test doubles ↩

  4. Testcontainers - Testcontainers - Docker containers for integration testing ↩

  5. End-to-End Testing - E2E Testing Guide - Cypress Documentation ↩

  6. Playwright - Playwright - Microsoft’s end-to-end testing framework ↩

  7. Cypress - Cypress - JavaScript E2E testing framework ↩

  8. Behavior-Driven Development (BDD) - North, D. (2006). Introducing BDD ↩

  9. Given-When-Then - Given-When-Then - Martin Fowler ↩

  10. Cucumber - Cucumber BDD Framework ↩

  11. SpecFlow - SpecFlow - BDD for .NET ↩

  12. pytest-bdd - pytest-bdd - BDD library for pytest ↩

  13. Performance Testing - Performance Testing Guidance - Microsoft Docs ↩

  14. pytest-benchmark - pytest-benchmark - Python benchmarking plugin ↩

  15. Criterion - Criterion.rs - Rust benchmarking library ↩

  16. testing.B - Go Benchmarks - Go’s built-in benchmarking ↩

  17. k6 - k6 - Modern load testing tool ↩ ↩2

  18. Locust - Locust - Python load testing framework ↩

  19. Gatling - Gatling - Load testing tool for web applications ↩

  20. Artillery - Artillery - Modern load testing toolkit ↩

  21. Thread Safety Testing - ThreadSanitizer - Google’s Race Detector ↩

  22. Go Race Detector - Data Race Detector - Go Documentation ↩

  23. ThreadSanitizer (TSAN) - ThreadSanitizer - Clang’s thread safety tool ↩

  24. Idempotence - Idempotence - RESTful API Design ↩

  25. Resilience Testing - Chaos Engineering Principles ↩

  26. Chaos Engineering - Basiri, A., et al. (2016). Chaos Engineering - Netflix Technology Blog ↩

  27. Circuit Breaker Pattern - Circuit Breaker - Martin Fowler ↩

  28. Compatibility Testing - Compatibility Testing Guide - Software Testing Help ↩

  29. Internationalization (i18n) Testing - Internationalization Testing - W3C ↩

  30. Data Integrity Testing - Database Testing Best Practices ↩

  31. Feature Flag Testing - Feature Toggles - Martin Fowler ↩

  32. Test Coverage - Marick, B. (1999). How to Misuse Code Coverage ↩

  33. Flaky Tests - Eradicating Non-Determinism in Tests - Martin Fowler ↩

  34. Test Fixtures - Test Fixture - xUnit Patterns ↩

  35. Test Factories - Factory Pattern for Test Data ↩

  36. Builder Pattern - Gamma, E., et al. (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Builder Pattern ↩

  37. Faker Libraries - Faker (JavaScript), Faker (Python) - Generate realistic fake data for testing ↩