|
| 1 | +--- |
| 2 | +title: "Why 100% Test Coverage Isn't Enough" |
| 3 | +summary: "The 100% Code Coverage Fallacy" |
| 4 | +date: 2026-04-07 |
| 5 | +tags: |
| 6 | +- SoftwareEngineering |
| 7 | +cover: |
| 8 | + image: "bad-release" |
| 9 | + alt: "Bad release" |
| 10 | +--- |
| 11 | + |
| 12 | +## Oops, I Broke the Release 😅 |
| 13 | + |
| 14 | +It's the notification that every developer dreads - A bug report landing soon after a fresh release. |
| 15 | + |
| 16 | +While implementing a feature enhancement, I accidentally introduced a critical regression that impacted new service onboarding. |
| 17 | +Although existing services continued to run smoothly, a bug in the configuration flow prevented new services from completing the process. |
| 18 | + |
| 19 | +## The Aftermath |
| 20 | + |
| 21 | +The issue was first reported by a single user, but the response that followed was heartening. |
| 22 | +Within a few hours, several others who encountered the same problem jumped in to help. |
| 23 | +Instead of just vague "it doesn't work" comments, the community provided me with a wealth of valuable troubleshooting data such as: |
| 24 | +- Environment and configuration details |
| 25 | +- Screenshots |
| 26 | +- Error messages and stack traces |
| 27 | +- Reproduction steps |
| 28 | + |
| 29 | +This collective effort enabled me to quickly pinpoint the root cause far quicker than I could have on my own. |
| 30 | +While I began working on a fix, affected users were advised to roll back to the last stable version. |
| 31 | +This also served as a reminder of the importance of having reliable backups and having a well-documented rollback process. |
| 32 | + |
| 33 | +## The "Naughty" Parentheses |
| 34 | + |
| 35 | +Fun-fact: The culprit was a sneaky pair of misplaced parentheses `()`. |
| 36 | + |
| 37 | +In Python, parentheses are overloaded. |
| 38 | +They are used for explicitly defining the **order of operations** (similar to Math where expressions within parentheses are evaluated first) and also for defining **tuples** (immutable collections). |
| 39 | +There was a set of misplaced parentheses which unintentionally transformed my logic into a tuple. |
| 40 | + |
| 41 | +Since the code was still syntactically valid, this bug was incredibly easy to overlook and slipped through the code review. |
| 42 | + |
| 43 | +## The Post-Mortem: The 100% Code Coverage Fallacy |
| 44 | + |
| 45 | +Part of the standard code review process was to verify that all code changes are covered by tests and a passing test suite. |
| 46 | +So, how did the bug slip through tests with 100% code coverage? |
| 47 | + |
| 48 | +**The Mock trap.** |
| 49 | + |
| 50 | +The tests relied heavily on mocks for API requests. |
| 51 | +The mocks didn't care whether the input was a single value or a tuple; it was programmed to return a success response. |
| 52 | + |
| 53 | +The crucial piece was missing assertions for inputs. |
| 54 | +If I had used `mock.assert_called_with(expected_input)`, the test would have failed and caught the issue before it made it to release. |
| 55 | + |
| 56 | +## Key Takeaways |
| 57 | + |
| 58 | +While code coverage is an important metric in testing, here are some limitations that let bugs slip through. |
| 59 | + |
| 60 | +**The Assertion Gap.** |
| 61 | +We often obsess over the *output* but neglect the *input*. |
| 62 | +This case was a prime example: the code ran, but because I didn't verify the specific arguments passed to the mock via `mock.assert_called_with()`, the logic failed silently. |
| 63 | + |
| 64 | +**Avoid shallow assertions.** |
| 65 | +Checking just the HTTP status code is not enough. |
| 66 | +You must verify the response headers and body to ensure the logic is working as intended. |
| 67 | + |
| 68 | +**Missing logic.** |
| 69 | +Code coverage only measures code that exists. |
| 70 | +It has no way of flagging the edge cases or validation steps you *forgot* to implement. |
| 71 | + |
| 72 | +--- |
| 73 | + |
| 74 | +PS: I’m incredibly grateful for a community that brings data instead of drama to a bug report. |
| 75 | +The fix is out now, and we’re back on track now, with better tests and stricter assertions. |
0 commit comments