While working with Docker builds, you may already be familiar with building images and creating containers:
$ docker build --build-arg FOO=BAR FOUX=BARS -t my-docker-image:latest .
$ docker run -d -p 8088:8080 my-docker-image
This would build and create a container for the following Dockerfile
:
FROM node:20-slim AS builder
WORKDIR /app
COPY . .
RUN yarn install --immutable --immutable-cache --check-cache
RUN yarn build:prod
FROM builder AS test
RUN yarn test:integration
FROM node:20-slim AS final
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/yarn.lock ./yarn.lock
RUN yarn install --production --frozen-lockfile
ENTRYPOINT ["node", "./dist/src/server.js"]
Need to run just the test layer in CI?
Enter the --target
flag:
$ docker build --target=test -t my-api:test .
The --target
flag will build the Dockerfile from the top to the specified stage, and then stop — skipping any further layers like the final production image. This is ideal when you only want to run tests in a CI/CD pipeline without building the full image.
CI/CD Example
- name: Build Docker image for testing
run: docker build --target=test -t my-api:test .
- name: Run integration tests in container
run: docker run --rm my-api:test
Benefits of this approach
- You maintain clean image separation and avoid adding test-only dependencies in the production image.
- Caching becomes smoother since your pipeline will cache the build layers and skip test runs in the production builds.
- It becomes clearer during debugging if the integration (or whatever test suite you want to run), since the test logs are isolated from the app runtime.