Negative testing
Deliberately giving a system bad or forbidden input to prove it rejects the request cleanly and leaves state unchanged.
See it
What it is
Negative testing deliberately supplies invalid, missing, unauthorized, or conflicting input and checks that the system refuses it correctly. The useful assertion is not merely 'it failed'. It is that the failure is controlled, explains the problem without leaking secrets, and leaves files, rows, balances, and queues in the right state.
Reach for it around validation, permissions, state transitions, and external boundaries. Try an expired token, a duplicate idempotency key, an impossible date, or a refund larger than the captured Stripe PaymentIntent amount. These cases turn assumptions about what cannot happen into executable checks.
Gotcha: throwing random garbage at a form is not a strategy. Derive negative cases from the contract and from risks, then assert the exact error and absence of side effects. Fuzz testing can explore a wider space, but it still needs clear invariants to decide what counts as a failure.
Ask AI for it
Write negative API tests for POST /users with Vitest and Supertest. Cover malformed JSON, missing required fields, an overlong email, a duplicate email, an expired JWT, and a valid JWT without the required role. Assert the exact HTTP status and RFC 9457 application/problem+json fields for each case, verify responses never expose stack traces or SQL details, and query Postgres after every request to prove no user or audit row was created.