Working with JSON and data
JSON is the language of API tests. Two functions are enough:
JSON.parse(text)— turns a JSON string into a JavaScript objectJSON.stringify(object)— turns an object into a JSON string; the second and third arguments give readable output:JSON.stringify(obj, null, 2)
In practice Playwright's response.json() parses for you — you rarely call JSON.parse by hand. You need it when working with raw text (a fixture read from a file, config in process.env).
CAREFUL: JSON.parse throws on invalid text. Trying to parse an empty body or an HTML error page drops the test with the cryptic Unexpected token < in JSON message — which actually means the server returned HTML, not JSON.
| Input value | After JSON.stringify | Consequence |
|---|---|---|
| an undefined field | the field disappears entirely | the difference between "absent" and "empty" is erased |
| a Date object | an ISO string | parsing back gives a string, not a Date |
| a function / method | dropped | a class instance degrades to a plain object |
| NaN, Infinity | null | a calculation error gets hidden |
| Map, Set | an empty object {} | the data is silently lost |
| a circular reference | throws a TypeError | hit when logging an object |
Reading deep data safely. When an API response is nested, use ?. and ?? instead of checking each level:
const city = body.items?.[0]?.shipping?.address?.city ?? 'unknown';
If a field is missing the test fails with a clear assertion message rather than Cannot read properties of undefined.
Checking the shape of a response (contract testing). Assert on structure and types, not exact values — such a test survives data changes but fails when the contract breaks:
- is it an array:
Array.isArray(body.items) - type:
typeof body.total === 'number' - key present:
'status' in order - Playwright/Jest matchers:
expect.objectContaining,expect.any(String),expect.arrayContaining
For deep copies, JSON.parse(JSON.stringify(obj)) is the old-school trick and causes every loss listed above. The modern answer is structuredClone(obj): it copies Date, Map, Set and nested arrays correctly (but not functions — it throws on those).
Prepare a short interview answer: "A JSON round-trip is not a deep copy, it is a lossy simplification: undefined and functions vanish, Date becomes a string, NaN becomes null. So I copy test data with `structuredClone` or a factory function."
📚 Sources and documentation
- JSONofficialdeveloper.mozilla.org