Testing Inside a DevContainer in CI

If CI runs tests on the raw runner instead of inside the devcontainer, it isn't testing the environment developers use. This page runs the suite inside the container with devcontainer exec, so the test toolchain, services, and dependencies exactly match local development.

Prerequisites

You need a devcontainer that builds in CI and a test command.

  • A committed devcontainer config that builds in CI.
  • A test command that runs inside the container.
  • Any services the tests need declared in the config/compose.

Test prerequisitesYou need to build the env, run tests via exec, start services, and surface the result.upbuild envexecrun testsServicesvia composeReportexit code

Step-by-Step Implementation

  1. Build the environment.
devcontainer up --workspace-folder .
  1. Run the suite inside the container.
devcontainer exec --workspace-folder . -- npm test
  1. Bring up dependent services if the tests need them (Compose devcontainer).
devcontainer up --workspace-folder .   # runServices starts db, cache, etc.
  1. Fail the job on a non-zero exit — exec propagates it automatically.

In-container test modelTests run inside the built container against real services, and the exit code gates CI.devcontainer upsame env as localdevcontainer exectests insideCompose servicesreal db/cacheExit codegates the job

Common Pitfalls

Test parity fails when tests run on the host or services aren't started.

Test triageA triage path from host-run tests to faithful in-container tests.Do tests run via devcontainer exec?NOMove them inside the containerAre dependent services up?YESExit code gates the jobCI tests match local

SymptomRoot CauseRemediation
Tests pass in CI, fail locallyCI tested the host, not the containerRun tests via devcontainer exec
Tests can't reach the databaseServices not startedStart them via the Compose devcontainer
Job stays green on failing testsExit code not propagatedLet exec return the test exit code
Different results each runUnpinned deps/runtimePin runtime + commit the lockfile

Conclusion

Run the suite where the code runs: inside the devcontainer, via devcontainer exec, against the same services and pinned toolchain developers use. Then a passing CI run is a real statement about the developer environment, not the runner's incidental setup.

In-container payoffTesting inside the container gives real parity and reproducible failures.Run insideSame toolchainSame servicesSame depsSo CI provesReal parityReproducible failsOne environment

FAQ

Why not just run tests on the CI runner? Because the runner's toolchain, services, and dependencies differ from the devcontainer developers use, so a green run says nothing about their environment. Running inside the container via devcontainer exec makes CI test the exact environment, so failures reproduce locally on the first try.

How do the tests reach a database in CI? Use a Compose-backed devcontainer so the database and other services start alongside the workspace, and run tests via exec inside the workspace container. The tests reach services by name on the shared network, exactly as they do locally.

Does a failing test fail the job? Yes, as long as you run via devcontainer exec, which propagates the command's exit code. A non-zero test exit becomes a non-zero job, so CI correctly fails. Avoid swallowing the exit code in a wrapper script.