Guides

How to Write API Test Cases: Template + Examples (2026)

Parveen KumariUpdated Aug 19, 20269 min read

Quick answer

A well-written API test case has five parts: an ID and title that name exactly what's being tested, a precondition (what state the system must be in), the request (method, endpoint, payload), the expected result (status code and response shape, not just "it works"), and a postcondition confirming no unintended side effects. Writing test cases in this template before automating them keeps coverage intentional — positive, negative, and boundary cases planned on purpose, not discovered by accident when something breaks in production.

Reviewed by Rishi Gaurav

Share:
Bar chart showing a typical breakdown of 8 to 15 test cases per endpoint across positive, negative, boundary, security, and idempotency categories

An API test case is a documented, specific scenario describing exactly what to send to an endpoint and exactly what should come back — distinct from the test automation code that later executes it. Writing the test case first, even in outline form, forces a decision about what "correct" means before you write the assertion that checks it, and produces something a non-engineer can review before any code exists.

This guide gives a reusable template, worked examples across positive, negative, boundary, and security scenarios, a naming convention, and how each written test case maps directly onto an automated test in whichever framework you use.

Table of Contents

  1. The API Test Case Template
  2. Naming Convention
  3. Positive Test Case Examples
  4. Negative Test Case Examples
  5. Boundary Test Case Examples
  6. Security Test Case Examples
  7. How Many Test Cases Does One Endpoint Need?
  8. From Test Case to Automated Test
  9. Common Mistakes When Writing Test Cases
  10. FAQ

Anatomy of an API test case showing ID, precondition, request, expected result, and postcondition

The API Test Case Template

FieldDescription
IDA unique, sortable identifier (e.g. TC_Users_Get_001)
TitleA one-line description of exactly what's being tested
PreconditionWhat state the system must be in before this test runs
Method & EndpointThe HTTP method and path under test
Request PayloadHeaders, query params, and body sent with the request
Expected Status CodeThe exact status code expected — not "success," a number
Expected ResponseThe response shape or specific field values expected
PostconditionWhat should (or should not) have changed as a result

Every field earns its place: skipping Precondition produces a flaky test that only passes when run in a specific order; skipping Postcondition misses side effects a status-code-only check can't catch (a DELETE that returns 200 but doesn't actually delete anything).

Naming Convention

A consistent pattern makes a test report scannable without opening each case individually:

TC_[Feature]_[Action]_[Condition]_[ExpectedResult]

Examples:

TC_Users_Get_ValidID_Returns200
TC_Users_Get_NonexistentID_Returns404
TC_Users_Create_MissingEmail_Returns400
TC_Users_Create_DuplicateEmail_Returns409
TC_Posts_Delete_UnauthorizedUser_Returns403

The exact format matters less than consistency across the whole suite — pick one, document it once, and every test case's purpose is readable from its name alone in a CI report.

Positive Test Case Examples

IDTitlePreconditionRequestExpected
TC_Users_Get_001Get an existing user by valid IDUser with ID 1 existsGET /users/1200, body matches user schema
TC_Posts_Create_001Create a post with valid required fieldsUser with ID 1 existsPOST /posts with {title, body, userId: 1}201, response echoes submitted fields plus a new id
TC_Posts_Update_001Update an existing post's titlePost with ID 1 existsPUT /posts/1 with updated title200, title reflects the new value

Negative Test Case Examples

IDTitlePreconditionRequestExpected
TC_Users_Get_002Get a user with a nonexistent IDUser with ID 999999 does not existGET /users/999999404, error body has consistent shape
TC_Posts_Create_002Create a post missing the required title fieldPOST /posts with {body, userId: 1} (no title)400/422, error message names the missing field
TC_Posts_Create_003Create a post with a wrong data typePOST /posts with userId: "one" (string, not integer)400/422, rejected before reaching business logic

Ready to shift left with your API testing?

Try our no-code API test automation platform free. Generate tests from OpenAPI, run in CI/CD, and scale quality.

Boundary Test Case Examples

Bugs cluster at the edges of valid ranges far more often than in the middle, which is why boundaries deserve their own dedicated cases rather than being left to chance:

IDTitlePreconditionRequestExpected
TC_Posts_List_001Request page 0 of a paginated listGET /posts?_page=0Defined behavior (either treated as page 1, or a 400) — whichever the spec documents, tested explicitly
TC_Posts_List_002Request a page number past the last pageOnly 10 pages existGET /posts?_page=999200 with an empty result array, not an error
TC_Posts_Create_004Submit a title at the maximum allowed lengthMax length is documented as 255 charsPOST /posts with a 255-char title201, accepted at exactly the limit
TC_Posts_Create_005Submit a title one character over the maximumPOST /posts with a 256-char title400/422, rejected just past the limit

Security Test Case Examples

IDTitlePreconditionRequestExpected
TC_Users_Get_003Access a resource without authenticationGET /account with no Authorization header401
TC_Users_Get_004Access another user's private resource (BOLA check)User A is authenticated; resource belongs to User BGET /users/{userB_id}/private-data as User A403 (not 200 with User B's data)
TC_Auth_Login_001Attempt login with an expired token on a protected routeToken issued more than its TTL agoGET /account with an expired Authorization header401, not a degraded but authenticated response

See our full checklist for the complete set of functional, security, and performance items these examples are drawn from, and the OWASP API Top 10 testing guide for deeper security-specific cases like these.

How Many Test Cases Does One Endpoint Need?

Enough to cover the happy path, every documented error status, key boundary values per input field, and at least one authorization case. For a typical CRUD endpoint, that's commonly 8–15 test cases:

  • 1–2 positive cases (valid input, valid ID)
  • 2–4 negative cases (missing fields, wrong types, invalid IDs)
  • 2–3 boundary cases (min/max length, empty, pagination edges)
  • 1–2 security cases (unauthenticated, unauthorized/BOLA)
  • 1–2 idempotency or side-effect cases (retry, verify postcondition)

Endpoints with more complex business logic — conditional validation, multi-step workflows, state machines — need proportionally more.

From Test Case to Automated Test

Each row in the tables above maps directly onto one automated assertion. TC_Users_Get_002 (nonexistent ID returns 404) in a few frameworks:

# pytest
def test_get_user_nonexistent_id_returns_404(api_session, base_url):
    response = api_session.get(f"{base_url}/users/999999", timeout=10)
    assert response.status_code == 404
// REST Assured
@Test
void getUserNonexistentIdReturns404() {
    given()
    .when()
        .get("/users/999999")
    .then()
        .statusCode(404);
}
# Karate
Scenario: get user with nonexistent ID returns 404
  Given path 'users/999999'
  When method get
  Then status 404

See the pytest, REST Assured, Playwright, Cypress, and Karate tutorials for the complete pattern in each framework — every one of them accepts test cases written in this template as direct input.

Free Guided worksheet

Build Your Testing Strategy in 30 Minutes

A structured worksheet that walks you through defining your testing strategy in 30 minutes. Cover architecture, tools, layers, and team responsibilities.

Download Free

Common Mistakes When Writing Test Cases

  • Writing "it works" as the expected result instead of the exact status code and response shape — an ambiguous expectation produces an ambiguous automated assertion.
  • Skipping the precondition, producing a test that only passes when run after another specific test happens to have created the data it needs.
  • Testing only the happy path per endpoint and treating negative/boundary cases as optional extras rather than an equal third of the suite.
  • No postcondition check, so a DELETE that returns 200 without actually deleting anything passes undetected.
  • Inconsistent naming across the suite, making a CI failure report unreadable without opening every individual case.
  • Writing test cases that describe implementation instead of behavior — "calls the UserService" instead of "returns 200 with the user's email field," which breaks the moment the implementation is refactored even though behavior is unchanged.

Frequently Asked Questions

What are the essential parts of an API test case? An ID and title, a precondition, the request itself, the expected result (status code and response shape), and a postcondition confirming no unintended side effects.

What is the difference between positive and negative test cases? A positive case sends valid input and expects success. A negative case deliberately sends invalid input and expects the API to reject it correctly.

What are boundary test cases in API testing? Cases targeting the edges of valid input ranges — minimums, maximums, empty values, and pagination edges — where bugs cluster more often than in the middle of a valid range.

How do I name API test cases consistently? A common pattern is TC_[Feature]_[Action]_[Condition]_[ExpectedResult]. The exact format matters less than applying it consistently across the suite.

How many test cases does one API endpoint need? Commonly 8–15 for a typical CRUD endpoint: the happy path, documented error statuses, boundary values, and at least one authorization case.

Should I write test cases before or after building the automated tests? Before, at least in outline — it forces a decision about what "correct" means before you write the assertion, and produces a reviewable artifact.

Key Takeaways

  • Precondition and postcondition are the most commonly skipped template fields — and the ones that catch flaky ordering bugs and silent side effects.
  • "It works" is not an expected result. Write the exact status code and response shape.
  • Positive, negative, and boundary cases are roughly equal thirds of a complete suite, not happy-path-plus-afterthoughts.
  • A consistent naming convention makes CI failure reports scannable without opening every case.
  • 8–15 test cases per typical CRUD endpoint is a reasonable baseline; complex business logic needs more.
  • Every written test case maps directly onto one automated assertion, regardless of which framework executes it.

Generate Test Cases Automatically from Your OpenAPI Spec

Writing 8-15 test cases per endpoint by hand scales linearly with your API's size. Total Shift Left generates positive, negative, and boundary test cases directly from your OpenAPI spec — the same structure as the template above, covering every endpoint automatically and regenerating when the spec changes.

Start your free trial to see generated test cases for your own API, or see plans and pricing if you're already evaluating.

Ready to shift left with your API testing?

Try our no-code API test automation platform free.