← Back to libraryQuestion 222 of 273
🔧CI/CDIntermediate

Running Unit, API and E2E Tests in CI

📌 Definition:

Running tests in CI means executing each test type in a headless, reproducible, non-interactive environment — installing dependencies deterministically, providing any services the tests need, and producing machine-readable results and artifacts for every run.

📖 Detailed Explanation:

Local test runs make assumptions that break in CI: an already-installed browser, a running database, a warm cache, an interactive terminal. In CI you make everything explicit. Use deterministic installs (npm ci, not npm install) so the lockfile is honored. Run test runners in non-interactive/CI mode (many watch by default). Install browser binaries and their system libraries for E2E (npx playwright install --with-deps). Provide dependencies for integration/API tests via service containers or mocks. And always emit results in a machine-readable format (JUnit/JSON) plus failure artifacts (screenshots, traces, logs), because when a CI test fails you can't just re-run it interactively — the artifacts are your only window into what happened.

🔑 Key Points:
  • Deterministic installs: npm ci honors the lockfile; npm install can drift
  • Force non-interactive/CI mode so runners don't hang in watch mode
  • E2E needs browsers + system libs installed explicitly on the runner
  • Provide DB/API dependencies via service containers or network mocks
  • Always emit machine-readable results + failure artifacts (you can't re-run interactively)
🌍 Real-World Example:

A suite passes locally but hangs forever on CI. The cause: the test script ran in watch mode waiting for keypresses. Adding CI=true (which puts the runner in single-run mode) and switching npm install to npm ci makes the job run deterministically and exit cleanly.

📎 Code Example:
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20, cache: 'npm' }
      - run: npm ci                       # deterministic install
      - run: npm test -- --ci --reporters=default --reporters=jest-junit  # non-interactive + JUnit
      # E2E: install browsers + OS deps before running
      - run: npx playwright install --with-deps
      - run: npx playwright test
      - uses: actions/upload-artifact@v4
        if: ${{ !cancelled() }}
        with: { name: test-results, path: 'test-results/' }
🎯 Scenario-Based Interview Question:

Scenario: A test suite is green locally but on CI it (a) sometimes installs different dependency versions, (b) occasionally hangs, and (c) when it fails, no one can tell why. Address all three problems.