A flaky API rarely fails because someone forgot to test it. It fails because the test that ran checked the wrong thing, or checked nothing at all beyond a 200 status code. A well-written API test case is the difference between catching a broken contract before release and explaining the outage afterward.
This guide walks through what an API test case is, the fields a good one needs, and how to write one from scratch. You will see a complete worked example for a login endpoint, then build the same test in Apidog without writing a script.
What an API test case actually is
An API test case is a defined set of inputs, actions, and expected outcomes used to confirm that one endpoint behaves correctly under one specific condition. It is narrower than a test scenario. A scenario says “verify user login.” A test case says “POST valid credentials to /auth/login and confirm a 200 response with a JWT in the body within 800 ms.”
Each case targets a single behavior. That focus matters. When a broad, multi-purpose test fails, you still have to investigate which part broke. When a focused case fails, the failure name tells you the answer. If you are still mapping how cases relate to scenarios and scripts, test scenario vs test case and test case vs test script draw the lines clearly.
API testing usually covers five angles, and your cases should spread across all of them:
- Functional: does the endpoint return the right data for valid input?
- Negative: does it reject bad input with the correct error and status code?
- Performance: does it respond inside an acceptable time budget?
- Security: does it enforce authentication, authorization, and input limits?
- Contract: does the response match the documented schema?
A test suite that only checks the happy path will pass right up until a real user sends an empty password field.
Why you need a test case template
Writing each case from a blank page produces inconsistent coverage. One engineer records expected status codes; another records response bodies; a third writes “should work.” A template fixes that by forcing the same structure every time.
A shared template gives you four concrete wins. Coverage becomes auditable, because every case has the same fields and gaps are visible. Onboarding speeds up, because a new tester reads one format instead of five. Cases become reusable across releases, since a structured case is easy to clone and adjust. And traceability holds, because each case links back to a requirement or endpoint.
Templates also make automation realistic. A structured case maps cleanly onto an automated test step; a vague one has to be rewritten before a machine can run it.
The anatomy of a strong API test case
A complete API test case template carries these fields. You do not need all of them for every case, but the core set is non-negotiable.
| Field | Purpose |
|---|---|
| Test case ID | Unique reference, e.g. AUTH-LOGIN-001 |
| Title | One line describing the behavior under test |
| Endpoint | Method and path, e.g. POST /auth/login |
| Preconditions | State required before running, e.g. “user exists” |
| Headers | Required request headers |
| Request body / params | Exact input payload |
| Expected status | The HTTP code you expect |
| Expected response | Body fields, schema, or values to assert |
| Expected response time | The latency budget |
| Priority | Critical, high, medium, low |
| Test type | Functional, negative, security, performance |
The fields that teams skip most often are expected response time and expected response body. Skipping them is how a test “passes” while returning a 200 with an empefty payload, or a correct payload three seconds too late.
How to write API test cases step by step
Read the API documentation first. Note the required parameters, the authentication method, the documented status codes, and the response schema. Every test case is a claim about documented behavior, so the docs are your source of truth. Tools that read an OpenAPI spec to generate test collections can give you a starting skeleton.
List the conditions worth testing. For a single endpoint that usually means: valid input, each missing required field, each malformed field, an unauthorized request, an expired token, and an oversized payload. Each condition becomes one case.
Prepare distinct test data. Negative cases need data that is wrong in exactly one way. Reusing the same payload across cases hides bugs, because you only ever exercise one path.
Write the expected outcome precisely. “Returns success” is not an assertion. “Returns 200, body contains a non-empty token string, expires_in equals 3600” is. Precision here is what makes the case worth running.
Add the case to a runnable suite. A case in a spreadsheet documents intent. A case in a test tool produces a pass or fail. Group related cases so the whole endpoint runs in one click; test suites exist for exactly this.
Keep cases current. When the API changes, the cases change with it. A stale case that asserts an old schema is worse than no case, because it fails for the wrong reason and trains the team to ignore red builds.
A worked example: testing a login endpoint
Take a standard authentication endpoint: POST /auth/login, which accepts an email and password and returns a JWT. Here are four cases that cover it properly.
AUTH-LOGIN-001: valid credentials (functional, critical)
- Endpoint:
POST /auth/login - Preconditions: a user exists with email
dana@example.com - Body:
{"email": "dana@example.com", "password": "Correct-Horse-9"} - Expected status:
200 - Expected response: body contains a non-empty
token(string),token_typeequalsBearer,expires_inequals3600 - Expected response time: under 800 ms
AUTH-LOGIN-002: wrong password (negative, critical)
- Body:
{"email": "dana@example.com", "password": "wrong"} - Expected status:
401 - Expected response:
errorequalsinvalid_credentials; notokenfield present - Note: the message must not reveal whether the email exists
AUTH-LOGIN-003: missing password field (negative, high)
- Body:
{"email": "dana@example.com"} - Expected status:
400 - Expected response:
errorequalsvalidation_error;detailsnames thepasswordfield
AUTH-LOGIN-004: malformed email (negative, medium)
- Body:
{"email": "not-an-email", "password": "Correct-Horse-9"} - Expected status:
400 - Expected response:
errorequalsvalidation_error
Four cases, one endpoint, and already you are checking the contract, the error shape, the status codes, and a latency budget. Add a security case for an SQL-injection string in the email field and you have a suite worth running on every commit.
Writing the same test case in Apidog
You can run all four cases above without writing a line of script. In Apidog, an API test case is built visually.
- Import or define the endpoint. Bring in
POST /auth/loginfrom an OpenAPI file, a Postman collection, or define it directly. The request schema becomes the basis for every assertion. - Add the request data. Enter the body for case AUTH-LOGIN-001. For data-driven coverage, attach a CSV or JSON file so one case runs against every credential row; this is how data-driven API testing replaces four near-identical cases with one.
- Set assertions visually. Click to assert that the status equals 200, that
tokenexists and is a non-empty string, thatexpires_inequals 3600, and that the response time stays under 800 ms. No scripting required. - Group cases into a test scenario. Add all four login cases to one scenario so the endpoint is fully exercised in a single run.
- Run and read the report. Apidog executes the scenario, generates a pass/fail report per case, and surfaces the exact assertion that broke. You can run the scenario locally, on a schedule, or inside CI.
The point is not that scripting is wrong; it is that a structured visual case is faster to write, easier to review, and harder to get subtly wrong. When you do need code, Apidog still lets you drop into custom scripts. For a fully script-free workflow, API testing without Postman covers the broader approach.
Common mistakes to avoid
Asserting only the status code. A 200 means the request was handled, not that the response was correct. Always assert body fields.
One giant case per endpoint. When it fails, you learn nothing. Split by condition.
Reused test data. If every negative case sends the same payload, you only test one failure mode.
No latency assertion. Performance regressions slip through silently when nothing measures response time.
Cases that never get automated. A documented case that no runner executes is a comment, not a test. Move cases into a suite that runs on every build; generating test scripts from an OpenAPI spec is a fast way to bootstrap that suite.
Download Apidog if you want to follow the example and build the four login cases yourself.
Frequently asked questions
How many test cases does one endpoint need? Enough to cover the happy path, every required-field failure, one auth failure, and one security probe. For a simple endpoint that is four to six cases; for a complex one it can be more.
Should I write cases before the API is built? Yes. Writing cases against the design, design-first, catches contract gaps early. Pair this with AI-assisted test case generation to draft a first set quickly, then refine by hand.
Spreadsheet or test tool for storing cases? A spreadsheet documents intent but cannot run. Keep cases in a tool that executes them and reports results, so a case is always either green or red.
What is the difference between a test case and a test scenario? A scenario is the high-level goal (“verify login”); a case is one concrete, runnable check within it. See test scenario vs test case.
