Resource library

QA Interview

Docker Interview Questions for SDETs

Docker interview questions for SDETs, with Dockerfile and Compose examples, Selenium Grid, Testcontainers, and CI answers for reproducible test environments.

2,564 words | Article schema | FAQ schema | Breadcrumb schema

Overview

For SDETs, Docker solved the oldest excuse in testing: it works on my machine. Containers package your tests, their runtime, browsers, and even a throwaway database into one reproducible unit that runs identically on a laptop, a teammate's machine, and a CI agent. That is why Docker questions now appear in nearly every serious SDET loop, usually right after the framework-design round.

This guide targets SDETs and automation engineers who use containers to build and run test environments. It goes past generic Docker trivia into the parts testers actually touch: writing a Dockerfile for a test runner, spinning up a Selenium Grid with Compose, using Testcontainers for real dependencies, and wiring all of it into a pipeline. Each question includes the answer an interviewer is listening for.

You will find concrete Dockerfile and Compose snippets, the commands worth memorizing, and the reasoning that shows you understand why containers matter for test reliability, not just how to type `docker run`. If your tests still depend on whatever happens to be installed on the box, this is the round that will expose it.

Why SDETs Are Asked About Docker

Interviewers probe Docker to find out whether your test environment is reproducible or accidental. A suite that passes because your laptop happens to have the right Node version, browser build, and database is a liability the moment it runs anywhere else. A candidate who containers their tests is telling the interviewer that any engineer or CI agent can get an identical environment with one command, which is the reliability foundation everything else sits on.

There is also a scaling signal. Containers are how you run browsers in parallel, stand up disposable databases, and keep CI fast. So a Docker question is often a stealth follow-up to the earlier how do you make the suite faster question. Show that you reach for containers to get clean, parallel, disposable environments, and you connect the two rounds in a way that reads as senior.

  • Docker proves your environment is reproducible, not accidental.
  • One command gives any machine an identical test environment.
  • Containers underpin parallel browsers and disposable databases.
  • It is often a stealth follow-up to the make-the-suite-faster question.

Core Concepts: Images vs Containers

Interviewers start by checking you know the vocabulary precisely. An image is an immutable, layered template (your app, dependencies, and runtime baked together); a container is a running instance of an image, an isolated process with its own filesystem view. The relationship is class versus object: one image, many containers. A registry (Docker Hub or a private one) stores and distributes images.

Be ready to contrast containers with virtual machines, a favorite. VMs virtualize hardware and run a full guest operating system, so they are heavy and slow to boot. Containers share the host kernel and isolate at the process level, so they start in milliseconds and are far lighter. For testing, that speed is the whole point: you can spin up a clean environment per test run cheaply, which is impractical with VMs.

  • Image equals an immutable layered template; container equals a running instance.
  • One image, many containers (class versus object).
  • Containers share the host kernel; VMs run a full guest OS, so containers are lighter.
  • A registry (Docker Hub or private) stores and distributes images.

Writing a Dockerfile for a Test Runner

You will likely be asked to sketch a Dockerfile, so keep it clean and explain each instruction. A minimal image for a Node-based Playwright suite reads: `FROM mcr.microsoft.com/playwright:v1.44.0-jammy`, then `WORKDIR /tests`, then `COPY package*.json ./`, then `RUN npm ci`, then `COPY . .`, and finally `CMD ["npm", "test"]`. Each line is a deliberate choice you can defend.

Walk it through: `FROM` picks a base image (here one that already ships browsers), `WORKDIR` sets the working directory, and copying `package*.json` and running `npm ci` before copying the rest is a deliberate layer-caching trick. `CMD` is the default command when the container starts. Note that `CMD` can be overridden at run time, whereas `ENTRYPOINT` fixes the executable, a distinction interviewers like to probe.

  • `FROM` sets the base image; pick one that already ships your browsers or JDK.
  • Copy dependency manifests and install before copying source for cache reuse.
  • `CMD` is the default command; `ENTRYPOINT` fixes the executable, `CMD` its arguments.
  • `RUN` executes at build time; `CMD` and `ENTRYPOINT` execute at container start.

Images, Layers, and Build Efficiency

A follow-up tests whether you understand why builds are slow or images bloated. Each Dockerfile instruction creates a cached layer; Docker reuses a layer if nothing above it changed. That is why you copy `package.json` and install dependencies before copying source code: your dependencies change rarely, so that expensive layer stays cached across the common case of only test code changing.

Bring up image size, because fat images slow every pull in CI. Techniques to name: start from a slim or alpine base, use multi-stage builds to compile in one stage and copy only the artifact into a lean final image, and add a `.dockerignore` so `node_modules`, `.git`, and test output never enter the build context. A lean image that pulls fast is a direct CI speed win, which ties Docker skill back to the QA goal of fast feedback.

  • Each instruction is a cached layer; order them stable-to-volatile for cache hits.
  • Install dependencies before copying source so code changes do not bust the cache.
  • Multi-stage builds and slim or alpine bases keep final images small.
  • `.dockerignore` keeps `node_modules`, `.git`, and reports out of the build context.

Volumes, Networking, and Environment

Testers hit two practical needs: getting results out of a container and letting containers talk to each other. For persistence, mount a volume with `docker run --rm -v $(pwd)/reports:/tests/reports my-tests`, which maps a host folder into the container so your JUnit XML and screenshots survive after the container exits. The `--rm` flag auto-removes the container afterward so you do not accumulate junk.

For networking, containers on the same Docker network reach each other by service name, not by localhost. This matters when your test container talks to an app container or a Selenium hub: the address is the container name, resolved by Docker built-in DNS. Configuration usually flows in through environment variables passed with `-e` or an env file, keeping the image generic and the config per-environment.

  • Mount a volume (`-v host:container`) to persist reports and screenshots.
  • `--rm` auto-cleans the container after the run.
  • Containers reach each other by service name on a shared network, not localhost.
  • Pass configuration via `-e` env vars or an env file to keep images generic.

Docker Compose for Test Environments

When a test needs more than one container (a browser grid, an app, a database), Compose is the answer, and interviewers expect you to name it. A `docker-compose.yml` declares multiple services that start together on a shared network with one `docker compose up`. It turns a multi-service test environment into a single versioned file anyone can run identically.

A useful example is a Selenium stack: a `selenium-hub` service on the `selenium/hub` image exposing port 4444, plus a `chrome` service on `selenium/node-chromium` with `depends_on: [selenium-hub]` and an environment variable `SE_EVENT_BUS_HOST=selenium-hub`. Explain that `depends_on` controls start order, the environment block points the browser node at the hub by service name, and the whole stack tears down with `docker compose down`. The pitch to the interviewer: Compose gives every engineer and the CI agent the exact same environment, killing works-on-my-machine failures.

  • Compose declares multiple services that start together with `docker compose up`.
  • Services find each other by name on the Compose network.
  • `depends_on` orders startup; the environment block injects per-service config.
  • One versioned file gives every machine an identical test environment.

Running Browsers: Selenium Grid in Containers

Cross-browser at scale is a classic SDET Docker use, so be specific. Running Selenium Grid in containers means a hub container plus browser node containers (Chrome, Firefox, Edge), each disposable. Your tests point their RemoteWebDriver at the hub URL, and you scale a browser by adding node replicas. Because nodes are containers, they start clean every run, which kills the state bleed that plagues long-lived Grid machines.

Mention the practical wins: set `--shm-size` to avoid Chrome crashing on limited shared memory, keep nodes disposable so a wedged browser is just recreated, and scale replicas for easy parallelism. This is also where you connect Docker to speed: containerized, parallel browsers are how you take a multi-hour cross-browser suite down to minutes, the exact scaling answer the framework round was fishing for.

  • Grid in Docker equals one hub container plus disposable browser node containers.
  • Tests target the hub via RemoteWebDriver; scale by adding node replicas.
  • Set `--shm-size` so Chrome does not crash on low shared memory.
  • Disposable nodes start clean each run, eliminating Grid state bleed.

Testcontainers: Real Dependencies On Demand

For senior SDET roles, Testcontainers is a strong card to play. It is a library (Java, .NET, Node, Go, and more) that spins up real dependencies as throwaway Docker containers directly from your test code: a real Postgres, Kafka, or Redis, started before the test and destroyed after. That gives you production-fidelity integration tests without mocking the database or sharing a fragile staging instance.

In Java it is as simple as constructing a `PostgreSQLContainer` for `postgres:16`, calling `start()`, reading `getJdbcUrl()`, pointing the app under test at that URL, and running assertions, with the container auto-removed at the end. The reason interviewers love this: it shows you test against real integrations while keeping tests isolated and repeatable, because each run gets a fresh database on a random port with no cleanup debt. Contrast it with a shared test DB where one run leftover data breaks the next; Testcontainers makes that class of flake disappear.

  • Testcontainers launches real dependencies as throwaway containers from test code.
  • Each test run gets a fresh Postgres, Kafka, or Redis on a random port.
  • Production-fidelity integration tests without mocks or a shared staging DB.
  • Automatic teardown removes the cleanup debt that causes cross-run flake.

Docker in CI and Debugging Containers

Tie it together: how does Docker fit your pipeline? The clean story is that CI builds your test image, runs the container (often via Compose to bring up dependencies), collects results from a mounted volume, and discards everything. Because the image is identical everywhere, a pass locally means a pass in CI, which is the reliability payoff of the whole approach.

Be ready to debug a container. Name the commands: `docker ps` to see what is running, `docker logs <id>` for output, `docker exec -it <id> sh` to open a shell inside a running container and poke around, and `docker inspect` for config and networking. The most common test failure, a container that exits immediately, is usually diagnosed with `docker logs`, so lead with that.

  • CI builds the image, runs it (often via Compose), collects results, discards all.
  • Identical images mean a local pass equals a CI pass, the core reliability win.
  • Debug with `docker ps`, `docker logs`, `docker exec -it <id> sh`, and `docker inspect`.
  • A container that exits instantly is usually explained by `docker logs`.

Scenario-Based Questions

Scenario: your tests pass locally but the container run cannot reach the app. Diagnose networking first: are both containers on the same network, and are you addressing the app by service name instead of localhost? Inside a container, localhost is the container itself, a mistake that catches everyone once. Scenario: the test image is 2 GB and slows every CI run. Attack it with a slimmer base, a multi-stage build, and a `.dockerignore`, then verify with `docker history` to see which layer is fat.

Scenario: Chrome keeps crashing in the container with no clear error. That is the classic shared-memory issue; raise `--shm-size` (or mount `/dev/shm`), and confirm via the container logs. Answering these shows you have actually operated containers, not just read about them, which is exactly the line interviewers are trying to draw between hands-on and theoretical.

  • Cannot reach the app: check the shared network and use service name, not localhost.
  • Fat image: slim base, multi-stage build, `.dockerignore`, inspect `docker history`.
  • Chrome crashing: raise `--shm-size` and confirm in the container logs.
  • These scenarios separate hands-on operators from Docker readers.

Judgment and Security Questions

You may get a judgment prompt: when is Docker not worth it? A mature answer admits containers add build and orchestration complexity, so for a tiny unit-test suite with no external dependencies, plain local runs may be simpler. The value grows with the number of moving parts: browsers, databases, message queues, and the need for identical environments across a team and CI.

Another common one: how do you keep test images secure and current? Talk about pinning base image versions for reproducibility, updating them on a schedule to pick up patches, scanning images for vulnerabilities, and never baking secrets into layers (they persist in the image history) but injecting them at run time. Showing you weigh cost, security, and maintenance marks you as senior, not just Docker-literate.

  • Docker earns its keep as dependencies and environment-parity needs grow.
  • For a tiny dependency-free suite, local runs can be simpler, and say so.
  • Pin base versions, patch on a schedule, and scan images for vulnerabilities.
  • Never bake secrets into layers; inject them at run time.

Frequently Asked Questions

What is the difference between a Docker image and a container?

An image is an immutable, layered template containing your code, dependencies, and runtime. A container is a running instance of that image, an isolated process with its own filesystem view. One image can spawn many containers, like a class and its objects.

Why do SDETs use Docker for testing?

Docker packages tests, their runtime, browsers, and even a throwaway database into one reproducible unit that runs identically everywhere. That eliminates works-on-my-machine failures, enables a clean environment per run, and makes parallel containerized browsers easy, which speeds up feedback.

What is the difference between CMD and ENTRYPOINT in a Dockerfile?

`ENTRYPOINT` sets the executable that always runs and is not easily overridden. `CMD` provides the default command or arguments and can be overridden at run time. A common pattern is `ENTRYPOINT` for the binary and `CMD` for its default arguments.

What is Testcontainers and why is it useful for testing?

Testcontainers is a library that starts real dependencies such as Postgres, Kafka, or Redis as throwaway Docker containers directly from test code, then destroys them afterward. It gives production-fidelity integration tests with full isolation, avoiding mocks and the flake of a shared test database.

How do you run Selenium Grid in Docker?

Run a hub container plus browser node containers (Chrome, Firefox), typically via Docker Compose. Tests target the hub through RemoteWebDriver, and you scale browsers by adding node replicas. Set `--shm-size` so Chrome does not crash on limited shared memory.

Why can my test container not connect to the application container?

Usually because it is addressing localhost. Inside a container, localhost is the container itself. Put both containers on the same Docker network and address the app by its service or container name, which Docker resolves via built-in DNS.

Related QAJobFit Guides