Test Automation

API Testing Checklist: 9 Checks for Auth, Errors, and Data Integrity

API Testing Checklist: 9 Checks for Auth, Errors, and Data Integrity

A creation request returns 201, the response contains an order ID, and the test passes. Yet the order could belong to the wrong tenant, contain a different quantity, or disappear on the next read. Repeating the request after a timeout could create another order. A status assertion catches none of those failures on its own.

An API testing checklist becomes useful when each check names the request variation, the expected response, and the business state that must follow. Start with a known valid request, change one condition at a time, and keep response validation separate from proof of the resulting state.

Table of Contents

Define the Endpoint Contract and Safe Test Data

Before writing assertions, agree on the endpoint's authentication scheme, object permissions, accepted inputs, response schemas, error behavior, persistence guarantees, and retry rules. An OpenAPI description can organize parts of this contract, but declaring a schema or security scheme does not prove that the server enforces it.

The running example below is an explicitly fictional, unexecuted orders API. Its expected results are proposed contract choices, not observed responses. Assume two test identities with separate tenant-owned orders, synchronous creation, and isolated fixtures in an authorized sandbox. No requests or load tests against this fictional application have been run.

The table gives the expected results in compact contract notation. For example, “integer1..100” means an integer from 1 through 100, “creation201” means creation returning HTTP 201, and “stringid” means an id field whose value is a string. The sections that follow explain the assertions and their limits. Status choices such as 422 for invalid quantity and 404 for concealed foreign records belong to this fictional contract; adapt them to the API you are responsible for testing.

Check

Concrete Expected Result in the Fictional Contract

Authentication

Protected fictional endpoint returns401 with applicable challenge for missing/invalid credentials and no mutation; valid authorized baseline succeeds. Token-profile cases require documented profile.

Resource Permissions

Fictional cross-owner read/update/delete returns documented404 concealment policy with no disclosure or mutation; permitted own-resource action succeeds.

Input Validation

Fictional quantity must be integer1..100:0,101,string and absent required quantity rejected422; unsupported request media type415; no row created. These are authored contract assumptions, not universal status requirements.

Response Schemas

Fictional creation201 body has stringid, integerquantity, tenant and enumstate; reject missing/wrong types while independently checking quantity/ownership against input. Schema success alone is insufficient.

Error Contracts

Fictional adopted problem+json has stable type and matching status, useful safe detail and correlation reference; no secrets/stack trace. Distinguish transport status from machine error identifier.

Persisted Writes

Fictional synchronous creation201 is followed by own-resourceGET returning expected stored quantity and tenant; rejected writes leave baseline unchanged. Eventual consistency would instead require documented terminal state/deadline and bounded polling, not arbitrary sleep.

Idempotent Retries

Fictional keyedPOST contract within documented retention window returns same resource without second business effect; changed payload409. Unknown timeout alone never authorizes a retry. Key scope/retention/concurrency and v1/v2 behavior must be specified.

Pagination

Fictional stable fixture ordered by uniqueid yields each permitted record exactly once when following opaque next tokens to explicit end; filters and authorization remain enforced. No claimed live-dataset snapshot without explicit contract.

Rate-Limit Behavior

Fictional sandbox quota boundary returns documented429 and Retry-After; client delays and bounds retries, does not mutate on rejected request. Scope/budget/reset and retry safety are explicit. GitHub403/429 is vendor example, not required universal code.

1. Authentication

Establish that a permitted identity can complete the baseline request before interpreting a rejected credential case. Otherwise, a broken endpoint or incorrect fixture can make every negative test appear successful. Then design separate cases for absent credentials, invalid credentials, and the token conditions required by the application's documented profile.

For the fictional protected endpoint, missing or invalid credentials must return 401, include an applicable WWW-Authenticate challenge, and leave state unchanged. RFC 9110 requires that challenge on a 401 response. Assert the header as well as the status and check that no order was created.

If the API uses JWT access tokens, cover configured integrity validation and the required issuer, audience, and lifetime rules. An expired token or wrong audience is a meaningful negative case when the adopted profile defines those requirements. Do not assume every JWT claim is mandatory in every token format, and do not mistake decoding a token for validating it. Use test credentials without placing secrets in reports.

2. Resource Permissions

Authentication identifies the caller; resource permissions determine what that caller may do to a particular order. OWASP's object-level authorization guidance calls for checking the requested action on the requested object. Passing an endpoint-access check or using unpredictable IDs does not establish those permissions.

Create an explicit caller, object, and action matrix using only accounts and records authorized for the sandbox. A caller who may read an order might still lack permission to update or delete it. Include a successful own-resource case alongside each relevant foreign-resource rejection so that a universally failing route cannot satisfy the matrix.

In the fictional contract, cross-owner reads, updates, and deletes return 404 to conceal existence. HTTP permits that policy, but another API may document 403 instead. Verify that the response discloses no foreign fields and that a rejected update or delete leaves the owner's record unchanged. A denial status with a completed mutation is a failed authorization check.

3. Input Validation

Change one field or transport condition at a time so the failure remains attributable. For the fictional quantity field, the agreed cases are:

  • Integer 1 and integer 100 are accepted boundary values.
  • Integer 0 and integer 101 are rejected as outside the allowed range.
  • A string value and an absent required quantity are rejected rather than silently accepted.
  • A request body using an unsupported media type is rejected separately from field validation.

Here, invalid quantity cases expect 422 and no created order; unsupported request media type expects 415. These are concrete expectations for this design, not a rule that every validation failure everywhere must return 422. Keep malformed body syntax separate from a syntactically valid body with an invalid field value.

OWASP recommends checking input length, range, format, and type. Extend the cases to other fields according to their actual constraints rather than generating arbitrary invalid payloads. Also distinguish Content-Type, which identifies the supplied body representation, from Accept, which expresses response preferences. One successful JSON parsing assertion cannot cover both.

4. Response Schemas

Validate the response selected by the actual status and media type, including error responses. For the fictional 201 result, the agreed schema requires a string id, an integer quantity, a tenant value, and a state from an allowed enumeration. Explicitly specify required fields and nullability; a field's existence does not establish that null is valid.

Then compare values against independently prepared expectations. An integer quantity can satisfy the schema while differing from the submitted quantity. A string tenant can be well formed while identifying somebody else's tenant. Derive expected ownership from the authorized fixture, not from the response being tested.

OpenAPI's schema and response descriptions support structural assertions. They do not prove a stored write. Keep the schema check focused enough that a failure identifies the incompatible field, then use a separate state check for persistence.

5. Error Contracts

Exercise representative documented failures: malformed input, a missing order, a conflict, and a controlled internal failure available through approved test facilities. The response should help a client decide what to do without exposing credentials, stack traces, or another tenant's data. Avoid triggering uncontrolled faults in a shared service to obtain an error sample.

The fictional API adopts application/problem+json. Assert its stable problem type, an HTTP status consistent with the generated status member, useful safe detail, and a correlation reference defined by this contract. RFC 9457 provides this representation; it does not require every API to adopt it or make all its optional members mandatory.

Treat the problem type as the machine-readable identifier rather than parsing the wording of detail. A human explanation can change without changing the underlying error category. Keep the actual HTTP status in the evidence as well: a plausible error body delivered with an unexpected success status can mislead generic HTTP clients.

6. Persisted Writes

The write response and a subsequent read answer different questions. The response describes what the write operation reported; readback checks what an authorized consumer can retrieve afterward. Playwright's API-testing documentation demonstrates separate server-state and postcondition assertions, a useful pattern regardless of the runner chosen.

For the fictional synchronous creation contract, design the sequence as follows:

  1. Establish the caller's permitted dataset and the known baseline before the write.
  2. Submit one valid creation request with a distinctive test-owned value.
  3. Read the returned resource ID through an authorized retrieval path and compare stored quantity and tenant with the independent expectations.
  4. Check that rejected variants leave the baseline unchanged, then clean up only records owned by the test.

Do not use the original creation body as the readback result or compare a response object with itself. Where business effects extend beyond the resource, specify additional permitted observations rather than assuming one GET proves everything.

This example assumes a synchronous 201 contract. An asynchronous acceptance or eventually consistent read model needs a documented completion condition, observation path, and deadline. Use bounded polling for that condition when appropriate. An arbitrary sleep followed by a passing read can hide a contract violation or make the result dependent on timing.

7. Idempotent Retries

A connection failure can leave the client unsure whether a write happened. Define retry behavior before automating recovery, because a second request may repeat a business operation that already succeeded. HTTP method semantics and an application's keyed POST mechanism provide different kinds of guarantees.

Define the Keyed Operation

RFC 9110 defines an idempotent method by its intended effect: repeating identical requests has the same intended effect as one request. Responses can differ. That definition does not make an ordinary POST safe to repeat merely because a client supplies an invented key header.

For the fictional keyed POST contract, repeating the same key and payload within the specified retention window must identify the same resource without a second business effect. Reusing that key with a different payload expects 409 here. The contract must also define key scope, expiry, and what happens when matching requests arrive concurrently. Test the agreed concurrent behavior without assuming the service caches every rejected attempt.

Vendor and version details require their own assertions. Stripe, for example, documents API v1 replay of the saved status and body after execution begins, while API v2 can retry incomplete work and return an updated response. Neither behavior establishes a universal key lifetime, mismatch status, or response-equality rule for other APIs.

Resolve an Unknown Write Outcome

A timeout alone does not authorize an automatic retry of a non-idempotent operation. Use the documented operation lookup or keyed recovery mechanism, if available, to determine what happened. When the outcome cannot be established and safe replay semantics are absent, retain an unresolved result for investigation rather than issuing a fresh creation request.

The expected evidence includes the operation identity and business-side-effect count, not merely two similar response bodies. A duplicate order hidden behind a repeated success message still fails the fictional contract.

8. Pagination

Use a fixed, isolated collection with known permitted IDs and a stable unique-id order. Follow the API's documented continuation until its explicit end condition, then compare the complete result with the expected collection. Check both missing records and duplicates; checking the number returned alone can miss one record replacing another.

Keep filters and other contractually fixed arguments consistent across continuation requests. Google's AIP-158 explicitly allows a page to contain fewer items than requested, including zero, before the collection ends. For an API following those semantics, a short page is not a stopping condition while a next token remains.

Treat tokens as opaque continuation values, not as proof of permission. An authorized sandbox case should verify that using another test identity's token does not expose that identity's orders. Authorization still applies to the request and returned resources.

The exact-once expectation here depends on the fictional stable fixture. A live collection changing between requests may have different consistency guarantees. Establish those guarantees before interpreting additions, removals, or reordered records as defects; do not infer snapshot isolation from the presence of a cursor.

9. Rate-Limit Behavior

Define whose budget is being consumed, which endpoints share it, how reset information is represented, and which response indicates rejection. Limits may apply to users, applications, resources, or other scopes. GitHub's REST documentation distinguishes primary and secondary limits and documents both 403 and 429 responses. A checklist that accepts only 429 for every vendor would reject documented behavior.

For the fictional API, design a preapproved low-threshold sandbox boundary case expecting 429 with Retry-After and no mutation for the rejected attempt. This is a proposed functional test, not a request to exhaust a production quota or run a load test. If a controlled boundary fixture is unavailable, leave that server behavior unverified rather than increasing traffic until something fails.

Check the client policy separately: wait according to the documented signal, bound retries, and preserve the operation's retry-safety rules. Retry-After can carry delay seconds or an HTTP date. Waiting the right interval does not make an unsafe write safe to replay. Avoid exact budget-decrement assertions unless the service contract supports them; GitHub specifically notes that regional processing can cause observed remaining counts to vary.

Record the Evidence Needed to Trust Each Result

Attach the contract version, credential role without secrets, fixture ownership, request variant, expected status and schema, expected business state, and actual observations to each eventual execution record. Include cleanup and any unresolved outcome so another engineer can distinguish an assertion failure from unavailable evidence.

Start with one endpoint and turn its most consequential write into a case with an independent state oracle. If the expected permissions, persistence, or retry rules remain ambiguous, resolve that contract gap before marking the endpoint covered. A response that looks successful is a starting observation, not sufficient evidence of a correct operation.

Related

More on Test Automation

Create traceable test cases, prepare release-specific test runs, and keep approved coverage current as the product changes.