All articles
QA AutomationCI-CDMockingDocker

Shift Left Testing — Why We Moved Testing From the End to the Start

We started with E2E automation on Staging: slow, flaky, and run by hand. Shift Left changed the whole equation — how it happened, and the skills it asks of you.

7 min read

When automation first arrived, we put the tests at the end of the chain: development finishes, it gets deployed to Staging, and then we run the tests.

And it worked — but it came with a set of pains that anyone who worked back then knows well. Shift Left is the solution that came out of exactly those pains.

How test automation started

The picture was roughly the same everywhere, and it's still like this in many companies and countries today:

  • Selenium with Java — the most famous duo, hands down
  • A completely separate project, in its own Repository, far from the product code
  • Tests running against a real environment like Staging
  • And running them was manual: the tester opens the project and hits Run

And it really was a big leap. For the first time we had a Regression that ran overnight instead of taking a week. But over time, the problems showed.

The pains everyone lived through

The tests were Flaky. Passing one time and failing the next, with the same code. Sometimes the cause was the environment itself — a service being slow or returning the wrong thing — and sometimes the test itself was written in a fragile way.

Test data was a nightmare. You need a user in a specific state, and an order in a specific condition. In big companies this alone was a battle: who creates the users? Who cleans them up afterwards? And what happens when someone else uses the same user at the same time?

Running stayed manual, with maintenance before every run. Every UI change broke Locators, so before you could run anything you had to go and fix them.

Everything was E2E. There were no levels — every check, however small, went through the entire system.

And in the end: re-run until it passes. Worse still, after all that, you'd still end up checking a few flows by hand because some tests didn't make it through the run.

Warning
Once you start re-running a test until it passes, you've lost the point of having it. A test you don't believe when it fails might as well not exist.

Then came Shift Left

The idea is simple: instead of testing at the end, push testing down and earlier — to the Unit and Integration levels, while a bug is still cheap and easy to find.

To make that happen, new tools and practices emerged that changed how the work looks:

Separate the frontend from the Backend

Instead of testing the frontend while it's wired to all the real services, you test it against a Mock Server that returns whatever responses you want. The side effect turned out to be the most important part: you could now write these tests in the same language as the frontend, and in its own Repository.

That point alone solved half the problem. The test now lives right next to the code it tests, so any change that breaks a test shows up in the same Pull Request — not a week later in another project.

And separate the Backend too

Exactly the same logic: you test the Backend on its own, with a temporary in-memory database that's created at run time and wiped afterwards, and data you plant yourself (Seeding) instead of begging for ready-made users on a shared environment.

And just like that, the question "who creates the test users?" disappeared — you do, on every run, from scratch.

And that's where CI/CD was born

Once everything was in one place — the code, the tests, and the Mocks — you could run the whole chain on any machine: install the app, Mock everything external it needs, and run the tests.

Need users? Mock them. Need a specific response from a service? Mock it.

And that's what made the tests:

  • Stable — no shared environment that someone else is messing with
  • Fast — seconds instead of hours
  • And most importantly: running on their own in the Pipeline, on every Pull Request, without anyone pressing a button

The shape of the pyramid after Shift Left

E2E Critical flows only — 10% Integration Most of your coverage lives here Unit The foundation, and the fastest to run

The rule of thumb: 90% of your coverage at the Unit and Integration levels, and only 10% E2E — limited to the most important flows: login, checkout, payment. The flows that, if they go down, take the product down with them.

What Shift Left asks of you as a tester

This isn't just a change of tools — it's a change of skills:

Skill Why you need it
Mocking and Mock servers The whole idea rests on it: isolating what you're testing from what you're not
Docker The tool whose usage exploded in this era — it lets you run any app on any machine exactly the same way
Test design Knowing what gets tested at which level, and resisting the temptation to test everything E2E
Synthetic Transactions Tests that run on Production itself for the most important flows

That last point deserves a pause. Synthetic Transactions are small tests that run against the live environment every few minutes and execute a critical flow — for example: log in, add a product to the cart, start checkout. And when one of them fails, that's an alarm for the whole team that there's a real problem on Production that needs immediate action — not a ticket that goes into the Backlog.

That way you have both ends covered: before the code ships, and after it ships.

A small example that explains the whole idea

Let's say there's a bug in the discount calculation: when the order value is zero, the discount comes out as a negative number.

The old way: the bug reaches Staging, and you catch it with an E2E test a week later. The test tells you "the checkout page shows the wrong number" — and that's it. Then someone has to open the Logs, trace the request, get to the responsible service, and find the function. Half a day at least, spread across several people.

With Shift Left: a unit test on the discount function fails within two seconds, on the developer's machine, before the code even reaches a Pull Request. And the failure message tells you the function name, the expected value, and the value that came back.

The exact same bug. The difference is where you caught it, and that difference multiplies the deeper down the chain it gets.

In the end

Shift Left isn't just testing early and calling it a day. It's moving testing to the cheapest place where you can find the bug. A bug you catch with a unit test costs you minutes; the same bug, once it reaches Production, costs you meetings, tickets, and apologies.

And notice a pattern that keeps repeating: every shift in the history of testing — from manual to automation, and from automation to Shift Left — didn't eliminate the tester, it just raised the bar on the skills expected of them. The people who learned Mocking, Docker, and test design when they came out are the ones designing the whole system today.

And the bar is about to rise again, right now. But that's a story for another article.

Want to go deeper?

This article gives you the idea. The courses take you from zero to actually doing it.

See the courses