Run Node.js tests in a container
Prerequisites
Complete all the previous sections of this guide, starting with Containerize a Node.js application.
Overview
Testing is a core part of building reliable software. Whether you're writing unit tests, integration tests, or end-to-end tests, running them consistently across environments matters. Docker makes this easy by giving you the same setup locally, in CI/CD, and during image builds.
Run tests when developing locally
The sample application uses Vitest for testing, and it already includes tests for React components, custom hooks, API routes, database operations, and utility functions.
Run tests locally (without Docker)
$ npm run test
Add test service to Docker Compose
To run tests in a containerized environment, you need to add a dedicated test service to your compose.yml file. Add the following service configuration:
services:
# ... existing services ...
# ========================================
# Test Service
# ========================================
app-test:
build:
context: .
dockerfile: Dockerfile
target: test
container_name: todoapp-test
environment:
NODE_ENV: test
POSTGRES_HOST: db
POSTGRES_PORT: 5432
POSTGRES_DB: todoapp_test
POSTGRES_USER: todoapp
POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-todoapp_password}'
depends_on:
db:
condition: service_healthy
command: ['npm', 'run', 'test:coverage']
networks:
- todoapp-network
profiles:
- testThis test service configuration:
- Builds from test stage: Uses the
testtarget from your multi-stage Dockerfile - Isolated test database: Uses a separate
todoapp_testdatabase for testing - Profile-based: Uses the
testprofile so it only runs when explicitly requested - Health dependency: Waits for the database to be healthy before starting tests
Run tests in a container
You can run tests using the dedicated test service:
$ docker compose up app-test --build
Or run tests against the development service:
$ docker compose run --rm app-dev npm run test
For a one-off test run with coverage:
$ docker compose run --rm app-dev npm run test:coverage
Run tests with coverage
To generate a coverage report:
$ npm run test:coverage
You should see output like the following:
> docker-nodejs-sample@1.0.0 test
> vitest --run
✓ src/server/__tests__/routes/todos.test.ts (5 tests) 16ms
✓ src/shared/utils/__tests__/validation.test.ts (15 tests) 6ms
✓ src/client/components/__tests__/LoadingSpinner.test.tsx (8 tests) 67ms
✓ src/server/database/__tests__/postgres.test.ts (13 tests) 136ms
✓ src/client/components/__tests__/ErrorMessage.test.tsx (8 tests) 127ms
✓ src/client/components/__tests__/TodoList.test.tsx (8 tests) 147ms
✓ src/client/components/__tests__/TodoItem.test.tsx (8 tests) 218ms
✓ src/client/__tests__/App.test.tsx (13 tests) 259ms
✓ src/client/components/__tests__/AddTodoForm.test.tsx (12 tests) 323ms
✓ src/client/hooks/__tests__/useTodos.test.ts (11 tests) 569ms
Test Files 9 passed (9)
Tests 88 passed (88)
Start at 20:57:19
Duration 4.41s (transform 1.79s, setup 2.66s, collect 5.38s, tests 4.61s, environment 14.07s, prepare 4.34s)
Test structure
The test suite covers:
- Client Components (
src/client/components/__tests__/): React component testing with React Testing Library - Custom Hooks (
src/client/hooks/__tests__/): React hooks testing with proper mocking - Server Routes (
src/server/__tests__/routes/): API endpoint testing - Database Layer (
src/server/database/__tests__/): PostgreSQL database operations testing - Utility Functions (
src/shared/utils/__tests__/): Validation and helper function testing - Integration Tests (
src/client/__tests__/): Full application integration testing
Run tests when building
To run tests during the Docker build process, you need to add a dedicated test stage to your Dockerfile. If you haven't already added this stage, add the following to your multi-stage Dockerfile:
# ========================================
# Test Stage
# ========================================
FROM build-deps AS test
# Set environment
ENV NODE_ENV=test \
CI=true
# Copy source files
COPY --chown=nodejs:nodejs . .
# Switch to non-root user
USER nodejs
# Run tests with coverage
CMD ["npm", "run", "test:coverage"]This test stage:
- Test environment: Sets
NODE_ENV=testandCI=truefor proper test execution - Non-root user: Runs tests as the
nodejsuser for security - Flexible execution: Uses
CMDinstead ofRUNto allow running tests during build or as a separate container - Coverage support: Configured to run tests with coverage reporting
Build and run tests during image build
To build an image that runs tests during the build process, you can create a custom Dockerfile or modify the existing one temporarily:
$ docker build --target test -t node-docker-image-test .
Run tests in a dedicated test container
The recommended approach is to use the test service defined in compose.yml:
$ docker compose --profile test up app-test --build
Or run it as a one-off container:
$ docker compose run --rm app-test
Run tests with coverage in CI/CD
For continuous integration, you can run tests with coverage:
$ docker build --target test --progress=plain --no-cache -t test-image .
$ docker run --rm test-image npm run test:coverage
You should see output containing the following:
✓ src/server/__tests__/routes/todos.test.ts (5 tests) 16ms
✓ src/shared/utils/__tests__/validation.test.ts (15 tests) 6ms
✓ src/client/components/__tests__/LoadingSpinner.test.tsx (8 tests) 67ms
✓ src/server/database/__tests__/postgres.test.ts (13 tests) 136ms
✓ src/client/components/__tests__/ErrorMessage.test.tsx (8 tests) 127ms
✓ src/client/components/__tests__/TodoList.test.tsx (8 tests) 147ms
✓ src/client/components/__tests__/TodoItem.test.tsx (8 tests) 218ms
✓ src/client/__tests__/App.test.tsx (13 tests) 259ms
✓ src/client/components/__tests__/AddTodoForm.test.tsx (12 tests) 323ms
✓ src/client/hooks/__tests__/useTodos.test.ts (11 tests) 569ms
Test Files 9 passed (9)
Tests 88 passed (88)
Start at 20:57:19
Duration 4.41s (transform 1.79s, setup 2.66s, collect 5.38s, tests 4.61s, environment 14.07s, prepare 4.34s)
Summary
In this section, you learned how to run tests when developing locally using Docker Compose and how to run tests when building your image.
Related information:
- Dockerfile reference – Understand all Dockerfile instructions and syntax.
- Best practices for writing Dockerfiles – Write efficient, maintainable, and secure Dockerfiles.
- Compose file reference – Learn the full syntax and options available for configuring services in
compose.yaml. docker compose runCLI reference – Run one-off commands in a service container.
Next steps
Next, you’ll learn how to set up a CI/CD pipeline using GitHub Actions.