0%

De standaardclient en statusklassen · eindproject

Eindproject: Project: een client met de standaardbibliotheek

get_json, classify en report werken, en je hebt ze scenario voor scenario tegen practice-api zien werken. Een uitvoering tegen een live dienst laat zien dat één geval één keer werkte. Tests op een vervangende verbinding laten meer zien: elke klasse, de foutbodies, een time-out en het sluiten, elke keer dat je ze uitvoert, zonder dat er een server nodig is. Dit project maakt de client af en bewijst hem met je eigen tests.

Wat de client belooft

Als dit project klaar is, houdt wire.py zich aan deze beloften, en je tests controleren ze:

  • get_json geeft het volledige record terug voor een antwoord van elke klasse: "status", "reason", "headers", "content_type", "retry_after", "body", "class" en "data".

  • De body van elke 4xx en 5xx wordt bewaard, en gedecodeerd in "data" als hij als gelabeld is.

  • Als er geen antwoord aankomt, verlaat TimeoutError get_json, en is de verbinding gesloten.

  • report maakt van een time-out een regel die geen status noemt, en laat elke andere mislukking door.

Als een van deze beloften nog niet klopt, herstel dat dan eerst. Als les 1 tot en met 5 allemaal geslaagd zijn, hoeft je code misschien niet te veranderen.

De twee fakes

Open fakes.py en lees de twee na fake_connect. scripted_response(status, reason, header_pairs, body) bouwt een antwoord: header_pairs is een van lijsten [name, value] in de volgorde waarin een server ze zou sturen, en getheader vindt een naam in elke schrijfwijze, zoals de echte doet. fake_make_connection(response, calls) geeft een vervanger terug voor http.client.HTTPConnection. Geef hem mee als make_connection=, en elke stap die je code zet, komt op volgorde in calls terecht:

{"call": "connect", "host": "practice-api", "port": 8080, "timeout": 2.0}
{"call": "request", "method": "GET", "url": "/v1/items/1", "body": None, "headers": {"X-Practice-Name": "http-with-python"}}
{"call": "close"}

Met None als antwoord werpt getresponse() TimeoutError op, zoals een stille server de echte laat doen. Alles wat een test nodig heeft, is er: een gescripte status, headers in elke schrijfwijze, een body, en een verslag van of de verbinding werd gesloten.

Eén test, uitgewerkt

Hier is de vorm van een test voor een succes, met de eerste twee asserts uitgeschreven:

def test_get_json_decodes_a_success():
    calls = []
    response = scripted_response(200, "OK", [["Content-Type", "application/json"]], b'{"id": 1}')
    record = wire.get_json("/v1/items/1", make_connection=fake_make_connection(response, calls))
    assert record["class"] == "success"
    assert record["data"] == {"id": 1}

Zet een antwoord en een lege lijst calls klaar, handel door wire.get_json aan te roepen, en controleer daarna met asserts het record en calls. Voeg fake_make_connection en scripted_response toe aan de regel from fakes import bovenaan tests/test_wire.py.

Voor een test die een verwacht, heeft het bestand het patroon al: test_a_close_inside_the_body_is_an_error roept de functie aan binnen try, keert terug vanuit de except-clausule voor de exception die het verwacht, en werpt na de try AssertionError op als de aanroep in plaats daarvan terugkeerde. calls[-1] is de laatst vastgelegde stap, en daar hoort een close te staan.

Mijlpalen

  1. Een succestest, zoals die hierboven, die ook het item connect in calls controleert en dat het laatste item de close is.

  2. Eén test voor een 4xx en één voor een 5xx, elk met een JSON-foutbody, die de klasse en de gedecodeerde "data" controleren.

  3. Een test met fake_make_connection(None, calls) die TimeoutError van get_json verwacht en daarna controleert dat calls eindigt met de close.

  4. Een test voor report: een gescripte 504 geeft een regel die met 504 begint, en een stille server geeft een regel die timed out zegt en geen 504 bevat.

  5. Alles wat verder het bewijzen waard is, zoals Retry-After in een vreemde schrijfwijze, of classify op een code die geen enkele lijst van jou noemt.

Schrijf elke test als een gewone functie def test_...(): zonder , zoals de andere tests in het bestand. Houd import wire, en roep binnen elke test wire.get_json(...), wire.report(...) en wire.classify(...) aan; voeg geen regel from wire import toe en sla die functies niet op onder namen bovenaan het bestand. Check my work zet via de andere versies van deze functies in om te zien of je tests dat merken, en een naam die met from is geïmporteerd, zou het origineel vasthouden.

Voer het uit

Voer de tests uit vanuit de projectmap:

python -m pytest -q

Elke test zou moeten slagen, ook die uit hoofdstuk 1 en 2, en geen enkele heeft practice-api nodig. Als je wilt, voer report dan ook uit over de routes in README.md; deze uitvoering is voor jou en wordt nooit beoordeeld:

python -c "
import wire
for target in ['/v1/items?page=1&page_size=2', '/v1/scenarios/rate-limit?attempt=3', '/v1/items/99', '/v1/scenarios/server-error', '/v1/scenarios/timeout?delay_ms=750']:
    print(target, wire.report(target, timeout=0.5))
"

De eerste vier regels noemen een status en zijn klasse: 200 success, daarna 429 client error, 404 client error en 500 server error. De laatste zegt dat je client een time-out kreeg, omdat een halve seconde korter is dan de 750 milliseconden van het scenario.

Druk daarna op Check my work. Je client leest nu elk antwoord dat practice-api kan sturen en zegt wat er gebeurde als er geen komt. In hoofdstuk 4 draait de verbinding om: je schrijft de server, en wire.py wordt het hulpmiddel dat je precies laat zien wat je server verstuurde.

Opdracht

Maak de client met de standaardbibliotheek af en bewijs hem met je eigen tests.

Zorg er in wire.py voor dat get_json het volledige record teruggeeft ("status", "reason", "headers", "content_type", "retry_after", "body", "class", "data") voor een antwoord van elke klasse, -foutbodies bewaart en decodeert, en TimeoutError doorlaat met de verbinding gesloten; dat classify de klasse noemt van elke code van 100 tot en met 599; en dat report alleen een time-out omzet in een regel. Als les 1 tot en met 5 geslaagd zijn, klopt dit misschien al.

Schrijf in tests/test_wire.py minstens vijf tests die wire.get_json, wire.report of wire.classify aanroepen, met fakes.scripted_response en fakes.fake_make_connection. Dek een succes, een 4xx en een 5xx met JSON-foutbodies, een time-out (fake_make_connection(None, calls)) gevolgd door een controle dat de verbinding gesloten werd, en report op een 504 en op een time-out. Schrijf gewone def test_...(): zonder . Houd import wire en roep de functies daarlangs aan binnen elke test; voeg geen regel from wire import toe en sla ze niet op onder namen bovenaan het bestand. Houd de tests die al in het bestand staan.

Voer python -m pytest -q uit in de .

Druk daarna op Check my work. Die roept get_json, classify en report aan met een vervangende verbinding en met statussen, headers en bodies die je niet hebt gezien. Daarna voert ze je tests uit, eerst tegen je eigen wire.py en daarna tegen vier kapotte clients: een die elke 4xx een succes noemt, een die de body van elke 4xx en 5xx weggooit, een die zijn eigen time-out meldt als 504, en een die de verbinding open laat als er geen antwoord aankomt. Je tests moeten allemaal slagen tegen de eerste, en minstens een moet mislukken tegen elk van de andere. Een test die naar het netwerk grijpt, mislukt in plaats van het te bereiken. Niets uit je terminal wordt beoordeeld.