Unit Tests passed. The bug shipped anyway.
The Pipeline was green. All the unit tests passed. All the E2E Tests passed. Yet the feature was broken.
Marco stared at the Slack message from support.
“Customer says they were charged $110 on a $100 order. Tax should have been $8, not $10.”
He pulled up the dashboard. All tests green. CI/CD pipeline — clean. Not a single failure in weeks.
And yet, the tax calculation was wrong.
Marco spent the next hour tracing the bug. The tax calculation itself was correct. The unit test proved it: pass in a rate, get back the right amount. No issues there.
The problem was simpler than that. And worse.
The developer who built the order service had hardcoded the tax rate as a constant — 10% — while working on the feature. The plan was to pull it from the country configuration later.
But “later” never came.
The feature shipped, the tests passed, and nobody noticed.
The unit test for the tax calculation? It tested the math. And the math was right. 10% of $100 is $10. Test passes.
But the feature was wrong. The customer was in a state with an 8% tax rate, and the system charged 10% because it never actually looked up the rate.
E2E tests? They had those too. But the E2E test just checked that the confirmation page appeared after placing an order. It never verified the actual total.
The feature was broken. And no test caught it.
Here’s the uncomfortable truth most teams don’t talk about:
Unit tests verify that individual components work. They can’t tell you whether the feature works. Marco’s tax calculation was correct — it just used the wrong input.
E2E tests verify the happy path through the UI. But they’re slow, fragile, and they often check that something shows up on screen, not that the right thing shows up.
There’s a massive gap between “all my unit tests pass” and “this feature actually works as the customer expects.”
That gap is where your production bugs live.
There’s a test type that fills this gap
It’s called an acceptance test.
Not another E2E test. Something fundamentally different.
An acceptance test is an executable specification of system behavior. It answers one question: does this feature work the way the business expects?
Here’s what Marco’s E2E test looked like:
@Test
void testPlaceOrder() {
driver.get(baseUrl + "/products");
driver.findElement(By.id("product-1")).click();
driver.findElement(By.id("add-to-cart")).click();
driver.findElement(By.id("quantity")).sendKeys("5");
driver.findElement(By.id("checkout")).click();
driver.findElement(By.id("place-order")).click();
WebElement confirmation = driver.findElement(By.id("confirmation"));
assertTrue(confirmation.isDisplayed());
}Brittle. Tied to the UI. Breaks if someone renames a button. And it only checks if the confirmation page shows up — it never verified the actual total.
Here’s the same scenario as an acceptance test:
@Test
void shouldCalculateCorrectTotalWithTaxRate() {
scenario
.given().product()
.withUnitPrice(20.00)
.and().country()
.withCode("US")
.withTaxRate(0.08)
.when().placeOrder()
.withQuantity(5)
.withCountry("US")
.then().shouldSucceed()
.and().order()
.hasBasePrice(100.00)
.hasTaxAmount(8.00)
.hasTotalPrice(108.00);
}No browser. No CSS selectors. No flaky waits.
Just a clear business scenario: given a product at $20, a quantity of 5, and a country with 8% tax — the base price should be $100, the tax should be $8, and the total should be $108.
This test would have caught Marco’s bug immediately. The system would return a total of $110 instead of $108 — test fails. The hardcoded constant would have been found before it ever reached a customer.
Marco’s team? After they introduced acceptance tests, customer-reported bugs dropped dramatically in the first few months. Not because they wrote more tests — but because they wrote the right tests.
Want to learn how to write tests like this?
I’m running a live workshop: Stop Shipping Bugs: Acceptance Tests Workshop.
4 hours. Two evenings. Live on Zoom, with me.
⚡I’ll walk you through the full architecture — DSL, Drivers, how it all fits together — and by the end, you’ll have written a real acceptance test from scenario to assertion.
📅 May 27-28, 5:00-7:00 PM CET
Limited spots. Register now with the early bird discount - 100 EUR off with code EARLYBIRD100
— Valentina

