One variable holds one value. A service response is not one value but a set: an order with a number, a total and a status, plus a list of items. The language has two containers for such sets, and almost all of a tester's work is work with them.
A list: a sequence in order
live example
statuses = ["NEW", "PAID", "SHIPPED"]
Run
Running examples is part of paid access. There the same code runs inside the article: editor, run and check next to the paragraph. Free week →
Square brackets, values separated by commas. Elements are numbered from zero — the main beginner trap:
print(statuses[0]) # NEW — the first
print(statuses[-1]) # SHIPPED — the last
print(len(statuses)) # 3
Add and check:
statuses.append("DELIVERED")
print("PAID" in statuses) # True
Asking for an element that does not exist gives IndexError: list index out of range. A useful error: it means there was less data than you expected — a common
finding in tests.
A dict: a value by name
live example
order = {
"id": "ord-042",
"status": "PAID",
"total": 4990,
}
print(order["status"]) # PAID
Run
Running examples is part of paid access. There the same code runs inside the article: editor, run and check next to the paragraph. Free week →
This is exactly what JSON is. A JSON response, once parsed, becomes a Python
dict one to one: same keys, same values. So "read a field from the response" is
just response["status"].
A field that may be missing
This is where most tests break. Asking for a missing key is an error:
print(order["discount"]) # KeyError: 'discount'
The test fails not because it found a defect but because it is poorly written. Two safe ways:
if "discount" in order:
print(order["discount"])
print(order.get("discount")) # None when absent
print(order.get("discount", 0)) # 0 when absent
get() is the workhorse — but careful: it hides the absence. If the contract
says the field must be there, check for it explicitly, otherwise your test stays
silent about a real defect.
Nesting
A real response looks about like this:
live example
response = {
"id": "ord-042",
"customer": {"id": "cus-01", "name": "Anna"},
"items": [
{"product": "Grinder", "qty": 1, "price": 4990},
{"product": "Lunchbox", "qty": 2, "price": 890},
],
"total": 6770,
}
response["customer"]["name"] # "Anna"
response["items"][0]["product"] # "Grinder"
len(response["items"]) # 2
Run
Running examples is part of paid access. There the same code runs inside the article: editor, run and check next to the paragraph. Free week →
Every bracket is one step deeper. When you get lost, split it into variables: it reads worse than one line, but you can see where it broke when it breaks.
List or dict
The rule is simple: a list when the elements are of one kind and order matters (order items, found records); a dict when every value has its own name (object fields).
What to solve
In short
- A list keeps order and is indexed from zero;
[-1]is the last element. - A dict maps name to value; parsed JSON is a dict, which is why reading a field
looks like
response["status"]. - A missing key raises
KeyError; useget()or anincheck — but verify contract-required fields explicitly. - Nesting reads step by step:
response["items"][0]["product"].