The Day I Realized My CI Pipeline Was Testing Nothing
Our pipeline was green for eight months straight. I used to feel good about that. Then one afternoon a teammate pushed a change that returned the wrong currency on a pricing endpoint, the pipeline went green anyway, and the bug shipped to production where a customer caught it before we did.
That is the moment I learned the difference between a pipeline that runs tests and a pipeline that verifies behavior. Ours had been doing the first thing and I had been assuming it did the second. Eight months of green had trained me to trust a signal that was mostly decorative.
What "green" was actually measuring
When I finally opened up what our CI was doing on each commit, the answer was almost funny. It was building the app, spinning up the service, and hitting a handful of endpoints to confirm they returned a 200. That is it. It never checked what was in the response. A pricing endpoint could return the wrong number, the wrong currency, or a cheerful little poem, and as long as it did so with a 200 status code, the pipeline clapped and moved on.
We had confused "the server did not fall over" with "the server did the right thing." Those are wildly different claims, and only one of them is worth putting on a dashboard.
The fix was not more infrastructure. It was learning to do api automation testing properly, which mostly comes down to one unglamorous discipline: every automated check has to assert something specific about the response, not just its existence. Status code, yes, but also the body, the values, the shape, and the behavior when the input is wrong. A test without an assertion is a test in costume.
Rebuilding the thing to actually bite
I gave myself a rule while rebuilding: if a test could not fail for a reason I could name in advance, it did not belong in the pipeline. That rule killed about half of what we had, which stung and was correct.
For the endpoints that survived, real automation meant defining, for each one, what a correct response looked like down to the field level, what the most likely wrong responses were, how it should behave with missing or malformed input, and how it handled an expired or absent auth token. Boring to write. Brutal in the best way when something breaks, because now a wrong currency turns the pipeline red in ninety seconds instead of turning up in a customer email three days later.
The other thing I had underestimated was tooling fit. I had been trying to force a request client built for manual poking into an automation role it was never designed for, which is a big part of why our checks were so shallow. Actually surveying the current api testing tools made it obvious there were options built specifically to live inside a pipeline, some of them able to generate assertion-rich tests from recorded traffic rather than making me hand-write every expected value. Picking the right family of tool for the job did more for our real coverage than any amount of willpower applied to the wrong one.
The signal that means something now
Here is the part I care about most, months later. Our pipeline is red more often than it used to be, and I consider that a massive upgrade.
A suite that never fails is not proof your code is perfect. It is proof your tests are asleep. The whole value of automation is a fast, trustworthy signal that turns red the instant behavior drifts from what you decided was correct. If it cannot go red, it cannot protect you, and green becomes a sedative instead of information.
These days when someone tells me their pipeline has been passing for months, I do not congratulate them anymore. I ask what the last meaningful failure was, and how long ago. If they cannot remember, we have found the problem, and it is not the code.
If you want to check your own
You do not need an incident to audit this. Pick one important endpoint, open the test that supposedly covers it, and ask a single question: what exact change to the response would make this test fail? If you can answer instantly, good, that is a real test. If you find yourself saying "well, if it returned an error," dig deeper, because "an error" usually means a non-200, and a wrong-but-cheerful 200 is exactly the bug that will walk straight through.
Then break it on purpose. Change a value, return the wrong field, and watch whether the pipeline notices. The first time you do this to a suite you trusted and it stays green, you will understand this post in your gut in a way no amount of me writing it can deliver.
Green should be earned. Make your tests capable of saying no.
What is the most embarrassing thing that ever sailed through a green pipeline on your team? I will go first, in the comments.
- Cars & Motorsport
- Art
- Causes
- Crafts
- Dance
- Drinks
- Film
- Fitness
- Food
- Games
- Gardening
- Health
- Home
- Literature
- Music
- Networking
- Other
- Party
- Religion
- Shopping
- Sports
- Theater
- Wellness
- IT, Cloud, Software and Technology