Test Automation in Docker: The Good, the Bad, and the WTF


"The Illusion of Consistency in Dockerized Tes Automation"

Hello folks, me again... knee-deep in another rabbit hole, banging my head against the Docker wall this time — and as always, writing about it once I crawl out the other side.

You know the drill: you dockerize your test automation suite, pat yourself on the back, and declare:

“Now it’ll run the same everywhere. We're finally consistent!”

But are we really?

Well, spoiler alert: not always.

Let’s talk about this magical thing we all chase called consistency — and why Docker, as amazing as it is, doesn’t always give it to us out of the box.

The Promise: One Image to Rule Them All

The dream is simple:
You wrap your entire test setup inside a Docker image — OS, tools, runtimes, configs, dependencies.
You push that image to Docker Hub/Artifactory/Any other Binary Management system you might use, run it in CI, pull it locally, drop it on a colleague’s machine, run it on a server farm — and everything behaves the same.

Sounds great, right?

Yeah… until it doesn’t.

Reality Check: Docker ≠ Isolation in All Cases

Docker does a fantastic job abstracting a lot of things. But it still shares the host’s kernel and sometimes behaves differently based on where and how you build or run your containers.

Here are some of the fun exceptions that I’ve personally tripped over:

Host OS and Architecture Differences

This one’s sneaky.

Let’s say you build your Docker image on an x86 machine, but your CI runs on ARM (hello, Apple M1/M2 laptops 👋). Boom — native binaries start misbehaving, Python packages with C extensions break, and tests that pass locally suddenly explode in CI.

You thought you had isolation — turns out you just had assumptions.

The solution by the way in this case would be to:

  • Use docker buildx to create multi-architecture images

  • Explicitly set the platform when you build or run:

docker buildx build --platform linux/amd64,linux/arm64 -t my-test-suite .

docker run --platform linux/amd64 my-test-suite


External Dependencies

You’re running tests in a container — great.
But if your test suite hits a real database, API, S3 bucket, or any remote system — you're no longer isolated. Network latency, DNS resolution quirks, missing VPN connections — all of these can lead to different results depending on where the container runs.

Solution?

  • Use mocks or spin up dependencies as containers via Docker Compose.

  • Control your network layer like you control your code.


Host OS Differences

I mean, that’s the whole point of Docker, right? Same container runs the same way on Windows, Mac, or Linux?

Well… not really.

Docker shares the host’s kernel, and that opens up a whole bag of fun when you're dealing with:

File Volumes

Let’s say you’re mounting a folder from your host into your container:

On Linux? Smooth sailing.
On macOS or Windows?
Good luck.

You’ll hit issues like:

File permissions not being respected

Line endings messing with your test inputs (especially in shell scripts or config files)

Symlinks breaking

Inotify not working — so tools that rely on file change detection just silently fail

And if your test automation writes logs, screenshots, reports, consumes assets or writes cache into a mounted volume — you might suddenly see different results depending on the host OS.


Networking – "Wait, localhost isn't localhost?"

Ah yes — the infamous Docker networking rabbit hole.

Here’s a riddle for you:

If your container calls localhost, who is it really calling?

  • On Linux, it might be the host.

  • On macOS or Windows with Docker Desktop, you’ll need to use host.docker.internal.

  • In Docker Compose with bridge mode? It might be a different service.

  • In CI/CD with DinD? Who knows…


So imagine this:

  • Your tests spin up a local mock server

  • Your app tries to hit it at localhost:3000

  • Works locally, fails in CI

Bottom line, don’t assume Docker = OS agnostic. It’s not. It’s still grounded in reality. Just wears a nice abstraction layer on top.


Test Flakiness Due to Resource Constraints

Your local machine has 16 cores and 32GB RAM.

Your CI agent has… not that.

Tests that pass locally might time out or flake in CI just because the underlying machine is slower, or there's CPU throttling, or other containers are hogging the host.



Mutable Dependencies and the “latest” Tag of Doom

Raise your hand if your Dockerfile has something like this:


Now raise your other hand if you don’t pin versions in requirements.txt.
Now slap yourself with both hands 😄. Just because it worked yesterday doesn't mean it’ll work today.

Lock your versions, build reproducible images, and stop trusting the latest tag.


So... Should We Abandon Docker?

Hell no.

Docker is awesome. It gives us:

  • Portable test environments

  • Predictable setups (if done right)

  • Faster onboarding

  • Cleaner pipelines

But like anything else in infrastructure, it's a tool — not a silver bullet. Also, as I always say - You should not do technology just for the sake of technology use. Everything should be done with proper prior thought and good design. Avoid overengineering things when it is not necessary. 

In any case - If you want true consistency, you still need:

  • Good engineering discipline

  • Version pinning

  • Clean separation of concerns

  • Controlled dependencies

  • Awareness of the runtime environment


Final Thoughts (Before You Go Back to Fighting Flaky Tests)

Docker helps you build fences. But it's up to you to make sure the fences actually hold.

So next time someone tells you,

“It’s fine, it’s Dockerized,”

You can smile, nod, and quietly double-check if they're running that container on an M1 Mac, using :latest, "voluming" files and calling real endpoints from inside their tests.

Because you know better now 😉


Want to rant or share your own war stories?
Drop me a message or comment below — I love hearing how other folks broke their own Docker illusions.

Until next time, keep testing smart. Not just hard.





Comments

Popular posts from this blog

Sharing is caring - Intro to Jenkins shared libraries

Test Automation, Security, and other vegetables

Safari Automation on AWS - what we learned from the process