<!--
title: Assert meaningful outcomes
domain: app-frameworks
topic: Testing
language: TypeScript
source: colinhacks/zod
updated: 2025-11-03
url: https://awesomereviewers.com/reviewers/zod-assert-meaningful-outcomes/
-->

When writing tests, validate the *actual user-visible behavior and structured error data*, not just a substring or a indirectly implied result. Make failures unambiguous (no swallow-pass patterns) and keep tests easy to debug.

Apply this standard:
- Prefer full message assertions (especially for formatted/translated strings) and assert structured fields like `error.path` / `error.details` when those are part of the contract.
- Avoid patterns that can accidentally always pass (e.g., empty `try/catch` without assertions inside).
- For behavior with control-flow semantics (e.g., `fatal`), add tests that prove the flow stops/continues, not only that an error exists.
- Cover realistic inputs and edge cases; add additional cases when current regex/validation logic is suspected incomplete.
- Improve debugging and maintenance with table-driven tests (`test.each` / equivalent) instead of ad-hoc concatenation.
- Where type-level expectations matter, add type tests (e.g., with `tsd`) rather than relying solely on compile-time errors.

Example (meaningful runtime assertions):
```ts
const result = schema.safeParse(badInput);
expect(result.success).toBe(false);
if (!result.success) {
  expect(result.error.issues[0].message).toBe("full expected message");
  expect(result.error.issues[0].path).toEqual(['points']);
}
```
Example (prove `fatal` stops):
```ts
const schema = z
  .object({ test: z.literal(true) })
  .nullable()
  .refine(v => v !== null, { message: 'foo', fatal: true })
  .superRefine((v, ctx) => { if (v && (v as any).test) ctx.addIssue({ code: 'custom', message: 'bar' }); });

const out = schema.safeParse(null);
expect(out.success).toBe(false);
expect(out.error.issues.map(i => i.message)).toEqual(['foo']);
```
