API Negative Testing: 20 Test Cases Beyond the Happy Path
A concrete negative testing matrix for malformed input, boundaries, authentication, authorization, and unintended side effects.
API negative testing sends invalid, incomplete, or unauthorized requests and checks that the API fails predictably without exposing data or performing unintended work. Run these cases against an owned demo API or an isolated test environment. The expected response must come from the API contract: HTTP defines status semantics, but it does not prescribe every validation rule or error body.
Use an owned demo endpoint
Assume a test service documents `POST /v1/orders` with JSON fields `customer_id` (string), `quantity` (integer from 1 to 20), and `sku` (string), and `GET /v1/orders/{id}` requires an authenticated test user. The host below is reserved for examples; replace it only with a service you own or may test.
POST /v1/orders HTTP/1.1
Host: api.example.test
Content-Type: application/json
Authorization: Bearer TEST_TOKEN
{"customer_id":"cust_demo","sku":"sku_basic","quantity":1}For every failed create, query the test database or a safe listing endpoint afterward and confirm no order was created unless the contract explicitly defines partial acceptance.
Twenty negative cases
| # | Input | Expected behavior | Reason |
|---|---|---|---|
| 1 | Omit `customer_id` | Validation error per contract; no order | Required field enforcement |
| 2 | Set `customer_id` to `null` | Reject or handle only if nullable by schema | Distinguish null from missing |
| 3 | Set `quantity` to `"one"` | Reject type mismatch | Prevent coercion surprises |
| 4 | Set `quantity` to `1.5` | Reject non-integer quantity | Enforce numeric shape |
| 5 | Send malformed JSON such as `{"sku":` | Client error, commonly 400 | Parser failure should be controlled |
| 6 | Set quantity to `0` | Reject below minimum | Lower boundary |
| 7 | Set quantity to `21` | Reject above maximum | Upper boundary |
| 8 | Set quantity to a huge integer | Reject safely or report range validation | Overflow and boundary handling |
| 9 | Set `sku` to an empty string | Reject if non-empty is required | Empty is not a useful identifier |
| 10 | Set `sku` to an extremely long string | Reject at documented length limit | Resource and storage limits |
| 11 | Send a body larger than the documented request limit | Reject, commonly 413 where applicable | Bound memory and parser work |
| 12 | Send valid JSON as `text/plain` | Reject or explicitly support it per contract | Content type handling |
| 13 | Send unsupported `Content-Type: application/xml` | Reject, commonly 415 | Unsupported media type |
| 14 | Omit the `Content-Type` header | Reject or apply documented default | Avoid ambiguous parsing |
| 15 | Omit the Authorization header | Authentication failure, commonly 401 | Verify credentials are required |
| 16 | Send an invalid or expired test token | Authentication failure per contract | Reject unusable credentials |
| 17 | Use a valid user token with no order-write permission | Deny per policy, commonly 403 | Authorization is separate from authentication |
| 18 | Read another test user's order ID | Deny or conceal existence per contract | Prevent object-level access leaks |
| 19 | Send an unsupported HTTP method to the route | Reject, commonly 405 with `Allow` where defined | Method behavior is explicit |
| 20 | Repeat a malformed create after a simulated client timeout | Still no unintended duplicate order | Failure and retry safety |
These status examples are conventions, not universal promises for every API. For example, an API may return 422 for schema validation, or 404 rather than 403 to hide a resource. RFC 9110 defines meanings for statuses such as 401, 403, 405, 413, and 415; your API contract determines which applies to a particular failure.
Send a few cases with curl
Missing field:
curl -i https://api.example.test/v1/orders \
-H 'Authorization: Bearer TEST_TOKEN' \
-H 'Content-Type: application/json' \
--data '{"sku":"sku_basic","quantity":1}'Wrong type:
curl -i https://api.example.test/v1/orders \
-H 'Authorization: Bearer TEST_TOKEN' \
-H 'Content-Type: application/json' \
--data '{"customer_id":"cust_demo","sku":"sku_basic","quantity":"one"}'Unauthorized resource access:
curl -i https://api.example.test/v1/orders/order_owned_by_other_test_user \
-H 'Authorization: Bearer USER_A_TEST_TOKEN'Use only disposable records and test credentials. Do not probe IDs or payload limits on a third-party service without written authorization.
Assert failure behavior, not just a status
For each case, verify the error schema, content type, safe message, and any request or trace ID. Check that stack traces, SQL details, secrets, and protected fields are absent. Then check state: no order, payment, email, inventory reservation, or downstream job should exist unless the contract says the operation was accepted. A 4xx response does not prove that no side effect occurred.
If a failure may happen after partial processing, use an idempotency key or a cleanup path for the fixture. Keep mutation tests isolated from production and make repeated execution safe.
Release checklist
- [ ] Map each required field, type, range, and content type to at least one negative case.
- [ ] Include missing, null, malformed, boundary, and oversized values.
- [ ] Test missing, invalid, expired, and under-scoped credentials.
- [ ] Test access to another user's resource using controlled test identities.
- [ ] Assert error body and headers against the documented schema.
- [ ] Verify no unintended writes, messages, charges, or jobs occurred.
- [ ] Confirm logs redact credentials and sensitive payload values.
- [ ] Repeat cases safely and record the contract version tested.
See how to test REST APIs, 401 vs 403 errors, and API authentication testing for request and access-control foundations.
References
Use API Test Lab to send your documented negative requests and inspect responses while keeping the target environment clearly identified.
More from the blog
Read 3 related articles from our latest posts.