Een echt project testen · oefening
Isoleer bestandsgedrag
Selectietests hebben geen blijvende gevolgen. Een JSON-schrijver wel, dus een bruikbare test moet bepalen waar die bytes mogen terechtkomen. Een vast bestand selected.json in het project zou tests afhankelijk maken van oude toestand en bestanden achterlaten.
Pytest geeft elke test die erom vraagt een nieuwe Path met de naam tmp_path. Voeg deze test van bestandsgedrag toe:
import json
from catalog import write_catalog_json
def test_json_file_keeps_visible_unicode(tmp_path):
# Arrange
output_path = tmp_path / "catalog-output.json"
item = {
"sku": "CAFE-7",
"name": "Café Cards",
"quantity": 7,
"unit_price": 2.5,
"in_stock": True,
}
# Act
write_catalog_json([item], output_path)
raw = output_path.read_text(encoding="utf-8")
# Assert with an independent JSON oracle and serialized text contract
parsed = json.loads(raw)
assert parsed == {"schema_version": 1, "items": [item]}
assert type(parsed["schema_version"]) is int
saved = parsed["items"][0]
assert type(saved["quantity"]) is int
assert type(saved["unit_price"]) is float
assert type(saved["in_stock"]) is bool
assert "Café Cards" in raw
assert "\\u00e9" not in raw
assert raw.endswith("\n")
assert not raw.endswith("\n\n")
Het verwachte document hoort bij de test. Het komt niet uit de teruggegeven waarde van de schrijver of uit de JSON-lezer van de cursist. De expliciete typecontroles zijn belangrijk omdat Python waarden als True en 1 anders als gelijk beschouwt. Als twee productiehulpfuncties dezelfde fout deelden, zou de ene de andere laten controleren schijnzekerheid geven.
Voer de volledige testsuite uit:
python -m pytest -q
Deze les moet rood eindigen. Het verzamelen blijft goed werken en de selectietests slagen, maar de nieuwe test faalt omdat de meegeleverde schrijver é omzet in de escape \\u00e9. Lees het gefaalde testgeval, de assertie en de werkelijke waarde van pytest. Bewerk catalog.py nog niet; die reparatie hoort bij les 5.
Controleer of je om de juiste reden rood bent. Je wilt status 1, waarbij de selectietests nog slagen en je nieuwe test faalt op de Unicode-assertie. Dat is gezond rood: de suite is uitgevoerd, heeft gevonden wat ze zocht en is het niet eens met de productiecode.
Status 2 of 5 zou een ander verhaal zijn. Die betekenen dat pytest je tests niet kon verzamelen of importeren, dus je hebt niets geleerd over het Unicode-contract. Een rode suite is alleen bewijs wanneer de fout degene is waarnaar je vroeg.
Nog één regel: geen enkele test mag in de projectmap schrijven, ook niet als die daarna opruimt. Opruimen gebeurt niet als een test halverwege faalt. Juist dan wil je niet dat het bestand van gisteren de uitvoering van morgen verwart. Daar is tmp_path voor.
Waarom schrijf je testuitvoer onder tmp_path in plaats van in het project?
Waarom vergelijk je met een dictionary die in de test is geschreven in plaats van je eigen JSON-lezer aan te roepen?
Opdracht
Voeg een verzamelde test toe die tmp_path vraagt, één nieuw Unicode-item via write_catalog_json schrijft, de echte tekst met json uit de standaardbibliotheek ontleedt en zichtbare Unicode plus precies één afsluitend regeleinde controleert. Lever in met correct verzamelde tests en die relevante test falend op het meegeleverde defect met Unicode-escapes; repareer de productiecode niet in deze les.