Skip to main content
Back to all posts

May 28, 2026 · 3 min read

The API tests worth keeping

Most of the cost of an API test suite is not writing the assertions. It is test data, environments, auth, and making a red build explain itself.

testingapisautomation

Writing a test that calls an endpoint and checks the status code takes five minutes. Multiply that across a large API surface and you have a lot of hours spent and, often, a suite nobody trusts.

I have spent enough time on test automation to be confident that the assertions are the cheap part. Everything expensive sits around them.

Where the time actually goes

In rough order of pain:

  1. Test data. Every meaningful assertion needs a known state behind it, and creating that state, isolating it from other tests, and cleaning it up afterwards is a bigger job than writing all the requests put together.
  2. Environments. A test that passes locally and fails in the pipeline is not a passing test. Chasing down config drift between the two teaches you more about a system than the endpoints do.
  3. Auth. Anything with roles has a large share of its real bugs sitting in the gap between "this endpoint works" and "this endpoint works for the right people only."
  4. The failures themselves. A red build that doesn't say why it's red just moves the manual testing to a different part of the day.

That last one changed how I write assertions everywhere.

# What you start with
assert response.status_code == 200

# What survives contact with a real suite
assert response.status_code == 200, (
    f"GET {path} returned {response.status_code}. "
    f"Body: {response.text[:300]}"
)

A failing test should read like a bug report, with the method, the path, the status, and a slice of the body sitting right there in the log. Anything less and the debugging session starts with somebody re-running the request by hand.

What "covered" should mean

Saying a suite covers an endpoint is vague enough to be useless. The version I find useful breaks down like this:

CheckQuestion it answers
Status codeDid the happy path work at all?
Response schemaDid the contract change underneath us?
Auth boundariesCan role X see role Y's data?
Error shapeDo failures return something a client can parse?

The schema check pulls the most weight. Endpoints rarely break loudly. They break by a field going null, a date changing format, an enum growing a value the consumer never handled. Asserting the shape of a response catches the class of regression that otherwise surfaces weeks later as somebody else's frontend bug.

Flaky is worse than missing

The hardest lesson here isn't technical.

A test suite people don't trust is worse than no test suite. They re-verify everything by hand anyway, and now you're paying for both.

A test that fails one run in ten sounds tolerable until the suite runs on every merge and someone has to decide, every time, whether red means broken or means Tuesday. People stop looking. Then a real failure scrolls past with the noise.

The fix is to treat flaky tests like incidents: quarantine immediately, fix or delete within days. Deleting a test you wrote feels wrong the first time. It stops feeling wrong once the remaining suite means something again.

The gate is the point

A suite starts paying for itself when it becomes a deployment gate rather than a report someone can read and ignore.

That is the part I would repeat anywhere. The tests catch bugs, and that matters. But the schedule only moves once shipping stops being a judgment call somebody has to make in a meeting.