Obsah
1. Testování webových aplikací s REST API z Pythonu
2. Instalace knihovny Requests pro aktuálně přihlášeného uživatele
3. Kontrola instalace knihovny Requests
4. Základ protokolu HTTP: metody a stavové kódy
5. Nejjednodušší příklad – poslání požadavku GET na zvolenou adresu
6. Základní atributy objektu typu Response: stavový kód a indikátor korektní odpovědi
7. Přečtení vybrané hlavičky z odpovědi HTTP serveru
8. Předání informací (parametrů) serveru přímo v URL
9. Přečtení těla odpovědi serveru v původní textové podobě
10. Získání metainformací o poslaných datech (typ a délka)
11. Zpracování odpovědi, která byla vrácena ve formátu JSON
13. Předání dat serveru ve „formuláři“
14. Předání dat serveru v těle požadavku
15. Vlastní jednoduchý testovací HTTP server
17. Požadavek poslaný na lokálně běžící HTTP server
18. Repositář s demonstračními příklady
19. Předchozí články s tématem testování (nejenom) v Pythonu
1. Testování webových aplikací s REST API z Pythonu
V úvodní části seriálu o testování aplikací s využitím Pythonu jsme si řekli, že v takzvané testovací pyramidě nalezneme velké množství typů testů:
- Jednotkové testy
- Testy komponent a integrační testy
- Systémové testy, akceptační testy
- Testy aplikačního (programového) rozhraní
- Testy grafického uživatelského rozhraní a end-to-end testy
- Testy chování (BDD)
Důležitým typem testů jsou testy aplikačního programového rozhraní, dnes typicky (ale nejenom) REST API. Tyto testy mohou být vyvinuty a spouštěny samostatně (například vůči jedné izolované komponentě), nebo mohou být součástí end-to-end testů zmíněných dále. Součástí API testů bývají i kontroly autentizace a autorizace, stejně jako kontroly, jak API reaguje na pokusy o průnik popř. „pouze“ na vadná data. Jak jsme si již řekli na začátku tohoto odstavce, dnes se velmi často používá REST API, takže testy pro ně lze vytvořit různými způsoby (například automaticky ze specifikace OpenAPI), ovšem používají se i další protokoly (MQTT, SOAP, RMI, SMTP atd.). Pro některé typy webových aplikací (resp. přesněji řečeno jejich REST API) mi vyhovovala kombinace knihovny Behave se známou knihovnou requests, ovšem existují i mnohé další více či méně komplexní nástroje.
Poměrně snadno se vytváří testy REST API založené na kombinaci již zmíněné knihovny Requests s frameworkem Pytest – typicky se jedná o testy typu „pošlu HTTP požadavek a očekávám tuto odpověď“. To, do jaké míry bude možné zjistit stav serveru, do značné míry záleží na konkrétním REST API rozhraní, ovšem pro mikroslužby komunikující přes REST API (kde se očekává jen limitovaný stavový prostor) může být kombinace Requests+Pytest dostatečná. Ovšem v dalších pokračováních tohoto seriálu se zaměříme i na další možnosti, poloautomatické vytváření testů na základě OpenAPI apod.
Dnes si popíšeme základní vlastnosti protokolu HTTP z hlediska uživatele. Demonstrační příklady budou vždy naprogramovány v Pythonu s využitím funkcí a metod z knihovny Requests, ovšem tam, kde to má smysl, bude ukázáno i alternativní použití velmi užitečného nástroje curl a v několika případech použijeme i nástroj telnet, v němž hlavičku a popř. i tělo požadavku vytvoříme ručně (což není příliš praktické, ovšem lépe se tak ukáže funkce HTTP na nižší úrovni, kterou před námi Requests i curl do jisté míry skrývá).
2. Instalace knihovny Requests pro aktuálně přihlášeného uživatele
Instalace knihovny Requests je velmi jednoduchá a použijeme pro ni nástroj pip resp. (podle aktuální konfigurace systému) pip3, tj. pip installer určený pro ekosystém programovacího jazyka Python 3. Instalaci provedeme s volbou –user, čímž zajistíme, že se všechny soubory nainstalují do adresáře ~/.local a pro instalaci tak nebude zapotřebí mít práva superuživatele:
$ pip3 install --user requests
Průběh instalace může vypadat následovně (jedná se o čistě nainstalovanou Fedoru 27 Server, proto se v ukázce instaluje poněkud postarší verze knihovny):
Collecting requests Downloading https://files.pythonhosted.org/packages/65/47/7e02164a2a3db50ed6d8a6ab1d6d60b69c4c3fdf57a284257925dfc12bda/requests-2.19.1-py2.py3-none-any.whl (91kB) 100% |████████████████████████████████| 92kB 296kB/s Collecting chardet<3.1.0,>=3.0.2 (from requests) Downloading https://files.pythonhosted.org/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/chardet-3.0.4-py2.py3-none-any.whl (133kB) 100% |████████████████████████████████| 143kB 898kB/s Collecting idna<2.8,>=2.5 (from requests) Downloading https://files.pythonhosted.org/packages/4b/2a/0276479a4b3caeb8a8c1af2f8e4355746a97fab05a372e4a2c6a6b876165/idna-2.7-py2.py3-none-any.whl (58kB) 100% |████████████████████████████████| 61kB 1.8MB/s Collecting urllib3<1.24,>=1.21.1 (from requests) Downloading https://files.pythonhosted.org/packages/bd/c9/6fdd990019071a4a32a5e7cb78a1d92c53851ef4f56f62a3486e6a7d8ffb/urllib3-1.23-py2.py3-none-any.whl (133kB) 100% |████████████████████████████████| 143kB 1.8MB/s Collecting certifi>=2017.4.17 (from requests) Downloading https://files.pythonhosted.org/packages/7c/e6/92ad559b7192d846975fc916b65f667c7b8c3a32bea7372340bfe9a15fa5/certifi-2018.4.16-py2.py3-none-any.whl (150kB) 100% |████████████████████████████████| 153kB 976kB/s Installing collected packages: chardet, idna, urllib3, certifi, requests Successfully installed certifi-2018.4.16 chardet-3.0.4 idna-2.7 requests-2.19.1 urllib3-1.23
Pokud je již knihovna Requests nainstalována, bude celý proces mnohem kratší:
$ pip3 install --user requests Requirement already satisfied (use --upgrade to upgrade): requests in /usr/lib/python3/dist-packages Cleaning up...
Alternativně je samozřejmě možné knihovnu nainstalovat do systémových adresářů, takže bude dostupná pro všechny uživatele:
$ sudo pip3 install requests
3. Kontrola instalace knihovny Requests
Po instalaci si můžeme ověřit, zda je knihovna Requests skutečně korektně nainstalována a zda k ní má interpret Pythonu přístup. Nejprve běžným způsobem spustíme interpret Pythonu:
$ python3 Python 3.6.6 (default, Jul 19 2018, 16:29:00) [GCC 7.3.1 20180303 (Red Hat 7.3.1-5)] on linux Type "help", "copyright", "credits" or "license" for more information.
Následně se pokusíme naimportovat knihovnu Requests a pro jistotu zobrazit i její dokumentaci:
>>> import requests >>> help("requests")
V případě, že instalace proběhla v pořádku, měl by výstup vypadat přibližně následovně:
Help on package requests: NAME requests DESCRIPTION Requests HTTP Library ~~~~~~~~~~~~~~~~~~~~~ Requests is an HTTP library, written in Python, for human beings. Basic GET usage: >>> import requests >>> r = requests.get('https://www.python.org') >>> r.status_code 200 >>> 'Python is a programming language' in r.content True ... or POST: >>> payload = dict(key1='value1', key2='value2') >>> r = requests.post('http://httpbin.org/post', data=payload) >>> print(r.text) { ... "form": { "key2": "value2", "key1": "value1" }, ... } The other HTTP methods are supported - see `requests.api`. Full documentation is at <http://python-requests.org>. :copyright: (c) 2017 by Kenneth Reitz. :license: Apache 2.0, see LICENSE for more details.
4. Základ protokolu HTTP: metody a stavové kódy
Protokol HTTP neboli Hypertext Transfer Protocol byl původně navržen pro přenos hypertextových dokumentů napsaných ve formátu/jazyku HTML. Dnes se ovšem používá i pro mnohé další účely; například ho využívají REST API služby atd. Protokol HTTP pracuje způsobem dotaz-odpověď neboli request-response (ostatně právě zde můžeme vysledovat původ názvu knihovny Requests).
Obrázek 1: Nejjednodušší použití protokolu HTTP – bezstavové poslání dotazu a obdržení odpovědi.
Jak dotaz, tak i odpověď, jsou reprezentovány formátovaným textem, kde jednotlivé řádky mají předem známý význam (bližší informace o formátu budou uvedeny níže). Požadavek většinou vyžaduje nějaký zdroj (resource), který je identifikován s využitím URL (Uniform Resource Locator). Celý URL se skládá z několika částí, z nichž mnohé mohou být vynechány:
userinfo host port ┌───────┴───────┐ ┌────┴────────┐ ┌┴┐ http://john.doe:password@www.example.com:123/forum/questions/?tag=networking&order=newest#top └─┬─┘ └───────────┬────────────────────────┘└─┬─────────────┘└────────┬──────────────────┘└┬─┘ scheme authority path query fragment (Zdroj: Wikipedia)
Protokolem HTTP je samozřejmě možné přenášet i data; ostatně pro tento účel slouží pojmenované metody s předem specifikovaným významem (viz tabulku pod tímto odstavcem). Specifikace protokolu HTTP rovněž obsahuje popis takzvaných stavových kódů, kterými server předává klientovi výsledek zpracování dotazu.
V následující tabulce je uveden přehled všech metod HTTP protokolu, přičemž nejpoužívanější jsou první dvě metody GET a POST, s jejichž použitím se seznámíme v demonstračních příkladech popsaných v navazujících kapitolách:
Metoda | Příklad použití |
---|---|
GET | Základní metoda sloužící k získání dat ze serveru. Může se například jednat o HTML stránku, statický obrázek, ale i výsledek volání REST API služby. |
POST | Metoda používaná pro odesílání dat na server. Teoreticky je sice možné použít i metodu GET, ovšem sémanticky je vhodnější použít tuto metodu (a REST API služby sémantiku operace většinou dodržují). |
PUT | Tato metoda slouží k nahrání dat na server. S touto metodou se setkáme u některých REST API služeb. |
DELETE | Slouží ke smazání dat ze serveru. Opět platí – s touto metodou se setkáme méně často u některých REST API služeb. |
HEAD | Tato metoda se částečně podobá metodě GET, ovšem server nevrátí požadovaná data, ale pouze metadata (čas změny, velikost dat, typ/formát dat apod.). Obecně je možné říci, že se tento dotaz zpracuje rychleji než GET. |
CONNECT | Používá se při použití TCP/IP tunelování. |
OPTIONS | Poslání dotazu na server, které metody podporuje. Využití najde například při použití CORS apod. |
TRACE | Server by měl klientovi odeslat požadavek zpět, čehož se používá pro zjištění, které údaje se mění na přenosové cestě. |
PATCH | Umožňuje změnu dat na serveru, má tady jinou sémantiku než DELETE+PUT. |
Stavový kód odpovědi serveru je reprezentován celým číslem, přičemž z první číslice (stovky) lze odvodit základní vlastnost stavu (úspěch, chyba, přesměrování…):
Skupina stavových kódů | Význam |
---|---|
1×x | informační, potvrzení atd. (ovšem požadavek se prozatím nevykonal) |
2×x | úspěšné vyřízení požadavku, popř. jeho akceptace serverem (202) |
3×x | přesměrování požadavku, informace o tom, že se objekt nezměnil atd. |
4×x | různé typy chyb typicky zaviněných klientem (bohužel nejrozsáhlejší skupina) |
5×x | různé chyby na serveru |
5. Nejjednodušší příklad – poslání požadavku GET na zvolenou adresu
V dnešním prvním demonstračním příkladu si ukážeme, jakým způsobem je možné použít základní HTTP metodu GET pro poslání požadavku na server. Použijeme přitom server dostupný na adrese http://httpbin.org/, který je možné využít pro otestování základních HTTP metod i jednoduchých REST API služeb. Konkrétně pošleme požadavek na adresu http://httpbin.org/get; v samotném požadavku nebudou předány žádné parametry. Dále se pokusíme stejný požadavek odeslat na neexistující URL http://httpbin.org/neexistuje.
Z příkazové řádky můžeme dotazy poslat pomocí univerzálního nástroje curl:
$ curl -v http://httpbin.org/get % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying 3.220.112.94... * TCP_NODELAY set * Connected to httpbin.org (3.220.112.94) port 80 (#0) > GET /get HTTP/1.1 > Host: httpbin.org > User-Agent: curl/7.55.1 > Accept: */* > < HTTP/1.1 200 OK < Date: Sun, 05 Jul 2020 18:46:02 GMT < Content-Type: application/json < Content-Length: 252 < Connection: keep-alive < Server: gunicorn/19.9.0 < Access-Control-Allow-Origin: * < Access-Control-Allow-Credentials: true < { [252 bytes data] 100 252 100 252 0 0 252 0 0:00:01 --:--:-- 0:00:01 672 * Connection #0 to host httpbin.org left intact { "args": {}, "headers": { "Accept": "*/*", "Host": "httpbin.org", "User-Agent": "curl/7.55.1", "X-Amzn-Trace-Id": "Root=1-5f021fea-fb5e443098df529029b36f40" }, "origin": "37.48.51.80", "url": "http://httpbin.org/get" }
Dotaz na neexistující zdroj mnoho serverů zpracuje takovým způsobem, že kromě HTTP kódu 404 navíc pošle i data, která jsou zobrazitelná ve webovém prohlížeči:
* Trying 3.220.112.94... * TCP_NODELAY set * Connected to httpbin.org (3.220.112.94) port 80 (#0) > GET /neexistuje HTTP/1.1 > Host: httpbin.org > User-Agent: curl/7.55.1 > Accept: */* > < HTTP/1.1 404 NOT FOUND < Date: Sun, 05 Jul 2020 18:53:50 GMT < Content-Type: text/html < Content-Length: 233 < Connection: keep-alive < Server: gunicorn/19.9.0 < Access-Control-Allow-Origin: * < Access-Control-Allow-Credentials: true < <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> <title>404 Not Found</title> <h1>Not Found</h1> <p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p> * Connection #0 to host httpbin.org left intact
curl je sice velmi užitečným nástrojem, ovšem některé operace před programátory skrývá. Můžeme však jít ještě níže a komunikovat s HTTP serverem s využitím starého dobrého telnetu. Nejprve se připojíme k HTTP serveru na port 80:
$ telnet httpbin.org 80 Trying 3.220.112.94... Connected to httpbin.org. Escape character is '^]'.
HTTP metoda GET se zadává řádkem GET, za nímž následuje cesta a popř. i parametry:
GET /get { "args": {}, "headers": { "Host": "a0207c42-pmhttpbin-pmhttpb-c018-592832243.us-east-1.elb.amazonaws.com", "X-Amzn-Trace-Id": "Root=1-5f0222d9-575a6990dba9ef7041fa18d8" }, "origin": "37.48.51.80", "url": "http://a0207c42-pmhttpbin-pmhttpb-c018-592832243.us-east-1.elb.amazonaws.com/get" } Connection closed by foreign host.
Nyní se již můžeme pokusit o použití knihovny Request a přepis předchozích dotazů do Pythonu. Je to velmi snadné, což je ostatně patrné i ze zdrojového kódu dnešního prvního demonstračního příkladu:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests # adresa s testovaci REST API sluzbou URL = "http://httpbin.org/get" # poslani HTTP dotazu typu GET response = requests.get(URL) # vypis objektu, ktery se vrati print(response) # nyni vyzkousime neexistujici endpoint: # adresa s testovaci REST API sluzbou URL = "http://httpbin.org/neexistuje" # poslani HTTP dotazu typu GET response = requests.get(URL) # vypis objektu, ktery se vrati print(response)
Po spuštění tohoto příkladu by se na standardním výstupu (tj. na konzoli či na emulátoru terminálu) měly objevit pouhé dva řádky. První z nich by měl obsahovat textovou podobu prvního objektu typu Response, který představuje úspěšnou odpověď serveru s HTTP kódem 200. Druhý řádek by měl obsahovat textovou podobu objektu typu Response s HTTP kódem 404, což je ovšem pochopitelné, protože jsme se snažili přistoupit k neexistující URL:
<Response [200]> <Response [404]>
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests # adresa s testovaci REST API sluzbou URL = "https://httpbin.org/get" # poslani HTTP dotazu typu GET response = requests.get(URL) # vypis objektu, ktery se vrati print(response) # nyni vyzkousime neexistujici endpoint: # adresa s testovaci REST API sluzbou URL = "https://httpbin.org/neexistuje" # poslani HTTP dotazu typu GET response = requests.get(URL) # vypis objektu, ktery se vrati print(response)
6. Základní atributy objektu typu Response: stavový kód a indikátor korektní odpovědi
Ve druhém demonstračním příkladu, který bude opět velmi jednoduchý, si ukážeme, jakým způsobem je možné zjistit stav (HTTP status) předaný v odpovědi. Z předchozích kapitol, ale i z běžných zkušeností s browserem, víme, že stavový kód HTTP je představován celým číslem doplněným o textový popisek stavu. V objektu typu Response představujícího odpověď serveru je číselný kód stavu uložen v atributu pojmenovaném status_code. Kromě toho existuje ještě atribut nazvaný ok, který obsahuje pravdivostní hodnotu True v případě, že je číselný kód stavu menší než 400 a hodnotu False v opačném případě. Použití tohoto atributu tedy může být poněkud problematické, protože například některé stavy 3×x je možné v kontextu vyvíjené aplikace považovat za chybové stavy:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests # adresa s testovaci REST API sluzbou URL = "http://httpbin.org/get" # poslani HTTP dotazu typu GET response = requests.get(URL) # vypis stavu odpovedi print(response.status_code) print(response.ok) # nyni vyzkousime neexistujici endpoint: # adresa s testovaci REST API sluzbou URL = "http://httpbin.org/neexistuje" # poslani HTTP dotazu typu GET response = requests.get(URL) # vypis stavu odpovedi print(response.status_code) print(response.ok)
Podívejme se, jaké hodnoty se vypíšou pro korektní URL https://httpbin.org/get a jaké hodnoty pro nekorektní URL https://httpbin.org/neexistuje. Pro korektní URL získáme podle očekávání stavový kód 200 a atribut ok bude mít hodnotu True:
200 True
Pro nekorektní URL je stavový kód HTTP roven 404 a tím pádem je i atribut ok nastaven na hodnotu False:
404 False
Můžeme použít i adresu, která vrací HTTP kód 500 značící interní chybu serveru:
$ curl -v http://httpbin.org/status/500 * Trying 3.220.112.94... * TCP_NODELAY set * Connected to httpbin.org (3.220.112.94) port 80 (#0) > GET /status/500 HTTP/1.1 > Host: httpbin.org > User-Agent: curl/7.55.1 > Accept: */* > < HTTP/1.1 500 INTERNAL SERVER ERROR < Date: Sun, 05 Jul 2020 19:16:31 GMT < Content-Type: text/html; charset=utf-8 < Content-Length: 0 < Connection: keep-alive < Server: gunicorn/19.9.0 < Access-Control-Allow-Origin: * < Access-Control-Allow-Credentials: true < * Connection #0 to host httpbin.org left intact
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests # adresa s testovaci REST API sluzbou URL = "http://httpbin.org/status/500" # poslani HTTP dotazu typu GET response = requests.get(URL) # vypis stavu odpovedi print(response.status_code) print(response.ok)
$ curl -v http://httpbin.org/status/999 * Trying 54.236.246.173... * TCP_NODELAY set * Connected to httpbin.org (54.236.246.173) port 80 (#0) > GET /status/999 HTTP/1.1 > Host: httpbin.org > User-Agent: curl/7.55.1 > Accept: */* > < HTTP/1.1 999 UNKNOWN < Date: Sun, 05 Jul 2020 19:19:01 GMT < Content-Type: text/html; charset=utf-8 < Content-Length: 0 < Connection: keep-alive < Server: gunicorn/19.9.0 < Access-Control-Allow-Origin: * < Access-Control-Allow-Credentials: true < * Connection #0 to host httpbin.org left intact
7. Přečtení vybrané hlavičky z odpovědi HTTP serveru
Odpověď serveru ve formě pouhého HTTP stavu (číselného kódu) samozřejmě většinou není příliš přínosná (kromě dotazů na to, zda se nějaký zdroj nezměnil), protože serveru posíláme dotaz za účelem získání nějakých dat (nebo změny stavu, ovšem v tomto případě se nepoužije metoda GET). Protokol HTTP je navržen takovým způsobem, že dokáže přenášet data v různých formátech, přičemž formát se rozpozná na základě hodnoty uložené do hlavičky pojmenované content-type (opět viz předchozí kapitoly s popisem této hlavičky). Údaje ze všech hlaviček získaných z odpovědi serveru je samozřejmě možné získat, protože objekt typu Response obsahuje mj. i atribut headers, ve kterém jsou všechny hlavičky uloženy ve formě slovníku (jméno hlavičky:hodnota). V dnešním třetím demonstračním příkladu je ukázáno, jakým způsobem se přistupuje právě k hlavičce content-type obsahující typ/formát dat, které server odeslal (nebo by měl odeslat) klientovi:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests # adresa s testovaci REST API sluzbou URL = "http://httpbin.org/get" # poslani HTTP dotazu typu GET response = requests.get(URL) # precteni hlavicek headers = response.headers # vypis typu internetoveho media print(headers.get("content-type"))
Webová služba dostupná na adrese http://httpbin.org/get nebo i https://httpbin.org/get vrací hodnoty uložené v těle odpovědi, přičemž tyto hodnoty jsou reprezentovány ve známém a velmi často využívaném formátu JSON. Služba je tedy správně nakonfigurována takovým způsobem, že vrací typ dat:
application/json
Naproti tomu jiné koncové body vrací data v odlišném formátu, což si opět můžeme velmi snadno otestovat:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests # adresa s testovaci REST API sluzbou URL = "http://httpbin.org/status/500" # poslani HTTP dotazu typu GET response = requests.get(URL) # precteni hlavicek headers = response.headers # vypis typu internetoveho media print(headers.get("content-type")) # tento koncový bod vrací obrázek URL = "http://httpbin.org/image/png" # poslani HTTP dotazu typu GET response = requests.get(URL) # precteni hlavicek headers = response.headers # vypis typu internetoveho media print(headers.get("content-type"))
S výsledky:
text/html; charset=utf-8 image/png
8. Předání informací (parametrů) serveru přímo v URL
Serveru, jehož služby potřebujeme přes knihovnu Requests využívat, je samozřejmě možné předat nějaká data. Protokol HTTP podporuje dva základní způsoby předání dat. Pokud se jedná o několik parametrů s relativně malým (krátkým) obsahem, je možné takové parametry předat přímo v URL (v prohlížeči přes adresní řádek). Zápis URL by v takovém případě měl vypadat následovně:
protokol://adresa.serveru/endpoint?parametr1=hodnota1¶metr2=hodnota2¶metr2=hodnota2
Konkrétně v našem konkrétním případě, kdy používáme testovací server http://httpbin.org/:
http://httpbin.org/get?x=6&y=7&answer=42
Příklad používající telnet ukáže, že se poslaná data objevují i v odpovědi serveru:
$ telnet httpbin.org 80 Trying 54.236.246.173... Connected to httpbin.org. Escape character is '^]'. GET /get?x=6&y=7&answer=42 { "args": { "answer": "42", "x": "6", "y": "7" }, "headers": { "Host": "a0207c42-pmhttpbin-pmhttpb-c018-592832243.us-east-1.elb.amazonaws.com", "X-Amzn-Trace-Id": "Root=1-5f022b53-28ef633486278da51c263ebf" }, "origin": "37.48.51.80", "url": "http://a0207c42-pmhttpbin-pmhttpb-c018-592832243.us-east-1.elb.amazonaws.com/get?x=6&y=7&answer=42" } Connection closed by foreign host.
Tento způsob přináší některá omezení. Zejména je nutné zajistit, aby se ve jménech a hodnotách parametrů nevyskytovaly některé znaky, které slouží jako oddělovače ve vlastní URL. Touto problematikou, kterou lze opět řešit automaticky, se budeme zabývat příště. Také je nutné zajistit (a zjistit), zda server neomezuje délku URL, například na 1024 znaků atd. (ovšem délku URL mnohdy omezují i prohlížeče).
Příklad, který serveru předá parametry přes URL, se prakticky žádným způsobem neodlišuje od prvního demonstračního příkladu, takže jen ve stručnosti:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests # adresa s testovaci REST API sluzbou URL = "http://httpbin.org/get?x=6&y=7&answer=42" # poslani HTTP dotazu typu GET response = requests.get(URL) # vypis objektu, ktery se vrati print(response)
9. Přečtení těla odpovědi serveru v původní textové podobě
Ve chvíli, kdy serveru předáme nějaká data (či parametry), server typicky odpoví tak, že klientovi pošle zpět vyžadované údaje. V tomto případě není možné tyto údaje předat v URL (ta je jen součástí dotazu, nikoli odpovědi), takže všechna vyžadovaná data server pošle v těle odpovědi a popř. do hlaviček doplní potřebná metadata (například již zmíněný content-type apod.). V odpovědi, která je v knihovně Requests reprezentována objektem typu Response, lze k nezpracovaným datům přistupovat přes atribut pojmenovaný text:
response = requests.get(URL) data = response.text
Opět se podívejme na jednoduchý demonstrační příklad, který testovacímu serveru pošle tři parametry x=6, y=7 a answer=42 a následně zobrazí odpověď serveru:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests # adresa s testovaci REST API sluzbou URL = "http://httpbin.org/get?x=6&y=7&answer=42" # poslani HTTP dotazu typu GET response = requests.get(URL) # vypis tela odpovedi print("Plain text:") print("-" * 60) # horizontalni oddelovac print(response.text) print("-" * 60) # horizontalni oddelovac
Příklad odpovědi vrácené serverem:
Plain text: ------------------------------------------------------------ { "args": { "answer": "42", "x": "6", "y": "7" }, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Host": "httpbin.org", "User-Agent": "python-requests/2.13.0" }, "origin": "213.175.37.10", "url": "https://httpbin.org/get?x=6&y=7&answer=42" } ------------------------------------------------------------
Zajímavá situace nastane ve chvíli, kdy server odešle komprimovaná data, konkrétně s využitím algoritmu gzip. Knihovna Request v tomto případě provede konverze za nás:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests # adresa s testovaci REST API sluzbou URL = "http://httpbin.org/gzip" # poslani HTTP dotazu typu GET response = requests.get(URL) # vypis tela odpovedi print("Plain text:") print("-" * 60) print(response.text) print("-" * 60)
Původní obsah (zkomprimovaný) získáme například opět nástrojem curl:
$ curl -v http://httpbin.org/gzip --output response.gzip
Obsah:
$ od -tx1 response.gzip 0000000 1f 8b 08 00 f8 ed 02 5f 02 ff 3d 8e 3d 0b 83 30 0000020 14 45 77 7f 85 64 94 26 7e d4 54 2d 74 70 28 6d 0000040 d7 62 a1 6b 8c cf 18 a8 46 62 5c 14 ff 7b a3 82 0000060 e3 3d ef bc cb 9d 1d d7 45 62 92 7d 0f 15 ba ba 0000100 46 8f 70 72 57 d6 00 ab 40 0f 96 cd 36 5a 90 73 0000120 0e bd b1 19 79 be 87 36 c9 d2 a7 1a 36 d6 18 d3 0000140 97 b2 23 4a 8b e3 f6 19 40 e3 5c 40 b7 19 7c d4 0000160 3f 3f 21 94 92 f0 30 be 38 6f a7 0e 17 9a 71 c0 0000200 af 75 00 7a 2b 65 6e 21 a6 75 10 41 55 a7 38 e6 0000220 94 65 94 56 9c b1 28 8c c2 a0 cc 4a 4a eb 4b 82 0000240 6c c3 b2 4f 6d c1 34 6a 7b 7e dc 8b bd 1b 29 2d 0000260 85 ec 56 76 4e 48 9c 92 8c 44 f1 05 39 8b f3 07 0000300 73 e7 44 fb f1 00 00 00 0000310
Po dekomprimaci:
$ zcat response.gzip { "gzipped": true, "headers": { "Accept": "*/*", "Host": "httpbin.org", "User-Agent": "curl/7.55.1", "X-Amzn-Trace-Id": "Root=1-5f02edf8-4c5a955dcaa21210b9b55f67" }, "method": "GET", "origin": "37.48.9.246" }
10. Získání metainformací o poslaných datech (typ a délka)
Zajímavé a v některých případech i užitečné bude zjištění základních metainformací o údajích, které nám server zaslal ve své odpovědi. Tyto metainformace se předávají formou hlaviček, a to zejména hlavičky content-type (typ/formát dat, již známe), content-length (délka dat) a popř. i date (datum vygenerování dat). Údaje z těchto hlaviček získáme velmi jednoduše, což je ostatně ukázáno i v pořadí již pátém demonstračním příkladu:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests # adresa s testovaci REST API sluzbou URL = "http://httpbin.org/get?x=6&y=7&answer=42" # poslani HTTP dotazu typu GET response = requests.get(URL) # precteni hlavicek headers = response.headers print("Metadata:") print("-" * 60) # vypis typu internetoveho media print("Typ dat:", headers.get("content-type")) # vypis delky dat predanych v tele print("Delka dat:", headers.get("content-length")) # vypis delky dat predanych v tele print("Datum:", headers.get("date")) print("-" * 60) # vypis tela odpovedi print("Plain text:") print("-" * 60) print(response.text) print("-" * 60)
Po spuštění tohoto demonstračního příkladu získáme přibližně následující výstup (ve vašem konkrétním případě bude odlišné datum a popř. i hlavička User-Agent):
Metadata: ------------------------------------------------------------ Typ dat: application/json Delka dat: 385 Datum: Sat, 04 Aug 2018 07:26:26 GMT ------------------------------------------------------------ Plain text: ------------------------------------------------------------ { "args": { "answer": "42", "x": "6", "y": "7" }, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate, compress", "Connection": "close", "Host": "httpbin.org", "User-Agent": "python-requests/2.2.1 CPython/3.4.3 Linux/3.13.0-139-lowlatency" }, "origin": "37.48.1.40", "url": "https://httpbin.org/get?x=6&y=7&answer=42" } ------------------------------------------------------------
11. Zpracování odpovědi, která byla vrácena ve formátu JSON
Mnoho webových služeb, především těch, které jsou postaveny na architektuře REST (REpresentational State Transfer), vrací údaje ve formátu JSON. Přesněji řečeno – odpovědi serveru obsahují stavovou informaci, všechny potřebné hlavičky s metainformacemi a taktéž tělo představující data serializovaná právě do formátu JSON. Ve skutečnosti je zpracování těchto dat s využitím knihovny Requests velmi jednoduché, protože lze využít metodu json() objektu typu Response. V případě, že server skutečně odeslal data ve formátu JSON, jsou tato data deserializována a vrácena programu ve formě seznamu či (častěji) slovníku, jehož prvky mohou být opět seznamy, slovníky, primitivní hodnoty atd. Následně je možné tato data zpracovat. V případě, že data nejsou ve formátu JSON, vyvolá se výjimka typu ValueError popř. json.decoder.JSONDecodeError:
# zpracovani odpovedi, ktera prisla ve formatu JSON data = response.json() # celý desrializovaný obsah JSONu print(data) # vybraná část args = data["args"] print(args) print("x =", args["x"]) print("y =", args["y"]) print("answer =", args["answer"])
Zpracování údajů vrácených testovacím serverem https://httpbin.org/ je ukázáno v dnešním šestém demonstračním příkladu:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests # adresa s testovaci REST API sluzbou URL = "http://httpbin.org/get?x=6&y=7&answer=42" # poslani HTTP dotazu typu GET response = requests.get(URL) # zpracovani odpovedi, ktera prisla ve formatu JSON data = response.json() print(data) args = data["args"] print(args) print("x =", args["x"]) print("y =", args["y"]) print("answer =", args["answer"])
Po spuštění tohoto příkladu by se nejprve měl vypsat obsah celého těla odpovědi (deserializovaný z JSONu):
{'args': {'answer': '42', 'x': '6', 'y': '7'}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'close', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.13.0'}, 'origin': '213.175.37.10', 'url': 'https://httpbin.org/get?x=6&y=7&answer=42'}
Následně by se měly vypsat jen vybrané údaje:
{'answer': '42', 'x': '6', 'y': '7'} x = 6 y = 7 answer = 42
Chyba, která vznikne při pokusu o zpracování odpovědi, která neobsahuje data ve formátu JSON:
File "06_response_json_error.py", line 13, in data = response.json() File "/usr/lib/python3.6/site-packages/requests/models.py", line 897, in json return complexjson.loads(self.text, **kwargs) File "/usr/lib64/python3.6/json/__init__.py", line 354, in loads return _default_decoder.decode(s) File "/usr/lib64/python3.6/json/decoder.py", line 339, in decode obj, end = self.raw_decode(s, idx=_w(s, 0).end()) File "/usr/lib64/python3.6/json/decoder.py", line 357, in raw_decode raise JSONDecodeError("Expecting value", s, err.value) from None json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
12. Použití HTTP metody POST
Kromě metody GET protokolu HTTP je samozřejmě možné v knihovně Requests použít i metodu POST. Tato metoda se typicky používá ve chvíli, kdy je zapotřebí předat serveru větší množství parametrů a/nebo rozsáhlejších dat (a její sémantika je navíc od GET odlišná, protože POST mění stav). Existuje několik způsobů, jak tato data předávat, ovšem v prvním příkladu, v němž metodu POST použijeme, se žádná data prozatím předávat nebudou. Úplný zdrojový kód tohoto příkladu vypadá následovně:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests # adresa s testovaci REST API sluzbou URL = "http://httpbin.org/post" # poslani HTTP dotazu typu POST response = requests.post(URL) # vypis odpovedi v plain textu print(response.text)
Zajímavá je odpověď serveru. Povšimněte si především toho, že nám server vrátil klíč form a json. K těmto klíčům se dostaneme později:
{ "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Content-Length": "0", "Host": "httpbin.org", "User-Agent": "python-requests/2.13.0" }, "json": null, "origin": "213.175.37.10", "url": "http://httpbin.org/post" }
Zdroj http://httpbin.org/post nepodporuje metodu GET, což si ostatně můžeme velmi snadno ověřit:
$ curl -v http://httpbin.org/post * Trying 3.220.112.94... * TCP_NODELAY set * Connected to httpbin.org (3.220.112.94) port 80 (#0) > GET /post HTTP/1.1 > Host: httpbin.org > User-Agent: curl/7.55.1 > Accept: */* > < HTTP/1.1 405 METHOD NOT ALLOWED < Date: Sun, 05 Jul 2020 19:38:12 GMT < Content-Type: text/html < Content-Length: 178 < Connection: keep-alive < Server: gunicorn/19.9.0 < Allow: OPTIONS, POST < Access-Control-Allow-Origin: * < Access-Control-Allow-Credentials: true < <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> <title>405 Method Not Allowed</title> <h1>Method Not Allowed</h1> <p>The method is not allowed for the requested URL.</p> * Connection #0 to host httpbin.org left intact
Zvolená HTTP metoda se v nástroji curl specifikuje parametrem -X:
$ curl -X POST -v http://httpbin.org/post * Trying 54.236.246.173... * TCP_NODELAY set * Connected to httpbin.org (54.236.246.173) port 80 (#0) > POST /post HTTP/1.1 > Host: httpbin.org > User-Agent: curl/7.55.1 > Accept: */* > < HTTP/1.1 200 OK < Date: Mon, 06 Jul 2020 09:47:33 GMT < Content-Type: application/json < Content-Length: 316 < Connection: keep-alive < Server: gunicorn/19.9.0 < Access-Control-Allow-Origin: * < Access-Control-Allow-Credentials: true < { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Host": "httpbin.org", "User-Agent": "curl/7.55.1", "X-Amzn-Trace-Id": "Root=1-5f02f335-41e814ac916d8e9484c52160" }, "json": null, "origin": "37.48.9.246", "url": "http://httpbin.org/post" } * Connection #0 to host httpbin.org left intact
13. Předání dat serveru ve „formuláři“
První metoda poslání parametrů od klienta na server používá takzvané „formulářové položky“. Tento poněkud nepřesný název je odvozen od toho, že se podobným způsobem posílají data z HTML formuláře (bez použití JavaScriptu, pouze čistě HTML prostředky). Pokud budeme chtít simulovat posílání dat tímto způsobem, můžeme použít nepovinný parametr nazvaný data předaný funkci requests.post():
payload = { "klic": "hodnota", "answer": 42, "question": None, "correct": True} # poslani HTTP dotazu typu POST se specifikaci hodnot formulare response = requests.post(URL, data=payload)
Tento způsob je použit i v dnešním osmém demonstračním příkladu, jehož úplný zdrojový kód vypadá následovně:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests # adresa s testovaci REST API sluzbou URL = "http://httpbin.org/post" payload = { "klic": "hodnota", "answer": 42, "question": None, "correct": True} # poslani HTTP dotazu typu POST se specifikaci hodnot formulare response = requests.post(URL, data=payload) # vypis tela odpovedi v plain textu print(response.text)
Odpověď serveru opět obsahuje položku form. Povšimněte si, že server získal a následně vrátil pouze tři hodnoty – chybí zde ovšem hodnota question=None, která se ve skutečnosti ve formulářových datech nepředala (neexistuje zde totiž ekvivalent pro speciální hodnoty None, nil či null):
{ "args": {}, "data": "", "files": {}, "form": { "answer": "42", "correct": "True", "klic": "hodnota" }, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Content-Length": "35", "Content-Type": "application/x-www-form-urlencoded", "Host": "httpbin.org", "User-Agent": "python-requests/2.13.0" }, "json": null, "origin": "213.175.37.10", "url": "https://httpbin.org/post" }
14. Předání dat serveru v těle požadavku
Pokud budeme chtít serveru předat větší množství strukturovaných dat, a to potenciálně včetně speciálních hodnot, je lepší takové údaje předat přímo v těle požadavku. Pro tento účel se ve funkci requests.post() použije nepovinný parametr nazvaný json a nikoli parametr pojmenovaný data (jako tomu bylo v příkladu předchozím). Opět se podívejme na jednoduchý demonstrační příklad, který se od příkladu předchozího odlišuje pouze v jediném detailu – nepovinném parametru funkce requests.post():
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests # adresa s testovaci REST API sluzbou URL = "http://httpbin.org/post" payload = { "klic": "hodnota", "answer": 42, "question": None, "correct": True} # poslani HTTP dotazu typu POST s telem response = requests.post(URL, json=payload) # vypis tela odpovedi v plain textu print(response.text)
Odpověď serveru nyní vypadá odlišně, protože nám testovací server v odpovědi říká, jak parametry získal (resp. jak mu byly předány). Povšimněte si, že nyní je pod klíčem form uložen prázdný slovník, ovšem naše parametry jsou nyní předány pod klíčem data a současně i ve zpracované podobě pod klíčem json:
{ "args": {}, "data": "{\"klic\": \"hodnota\", \"answer\": 42, \"question\": null, \"correct\": true}", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Content-Length": "68", "Content-Type": "application/json", "Host": "httpbin.org", "User-Agent": "python-requests/2.13.0" }, "json": { "answer": 42, "correct": true, "klic": "hodnota", "question": null }, "origin": "213.175.37.10", "url": "https://httpbin.org/post" }
Podobným způsobem lze použít i nástroj curl, samotná data jsou v tomto případě uložena v souboru data.json:
{ "klic": "hodnota", "answer": 42, "question": null, "correct": true }
Poslání dat společně se zobrazením odpovědi:
$ curl -X POST -v http://httpbin.org/post -d @data.json -H "Content-Type: application/json" * Trying 3.220.112.94... * TCP_NODELAY set * Connected to httpbin.org (3.220.112.94) port 80 (#0) > POST /post HTTP/1.1 > Host: httpbin.org > User-Agent: curl/7.55.1 > Accept: */* > Content-Type: application/json > Content-Length: 81 > * upload completely sent off: 81 out of 81 bytes < HTTP/1.1 200 OK < Date: Mon, 06 Jul 2020 09:56:07 GMT < Content-Type: application/json < Content-Length: 564 < Connection: keep-alive < Server: gunicorn/19.9.0 < Access-Control-Allow-Origin: * < Access-Control-Allow-Credentials: true < { "args": {}, "data": "{ \"klic\": \"hodnota\", \"answer\": 42, \"question\": null, \"correct\": true}", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Content-Length": "81", "Content-Type": "application/json", "Host": "httpbin.org", "User-Agent": "curl/7.55.1", "X-Amzn-Trace-Id": "Root=1-5f02f537-4a20ddc8bee8dabcacb6787a" }, "json": { "answer": 42, "correct": true, "klic": "hodnota", "question": null }, "origin": "37.48.9.246", "url": "http://httpbin.org/post" } * Connection #0 to host httpbin.org left intact
15. Vlastní jednoduchý testovací HTTP server
Aby bylo možné zjistit, jak přesně vlastně vypadá požadavek posílaný z klienta na server pomocí metody GET nebo POST, vytvoříme si vlastní velmi jednoduchou implementaci HTTP serveru založenou na existující (velmi užitečné) třídě BaseHTTPRequestHandler. Ta samozřejmě nebude v žádném případě určena pro produkční nasazení, protože například nijak neřeší HTTPS, souběžné zpracování většího množství požadavků, zabezpečení, autorizaci, kontrolu korektnosti požadavků atd. Server pouze velmi jednoduše zpracuje všechny HTTP požadavky typu GET a POST; klientovi přitom vrátí odpověď se stavem 200 OK a jednořádkovou (plain textovou) zprávou, která klienta pouze informuje o tom, jakou HTTP metodu při volání serveru použil. Zcela základní implementace serveru by tedy mohla vypadat následovně (bez importů, spuštění atd.):
class SimpleServer(BaseHTTPRequestHandler): def do_GET(self): # priprava hlavicky odpovedi self.send_response(200) self.send_header('Content-type', 'text/plain') self.end_headers() # odpoved serveru klientovi self.wfile.write("*** get ***".encode("utf-8")) def do_POST(self): # priprava hlavicky odpovedi self.send_response(200) self.send_header('Content-type', 'text/plain') self.end_headers() # odpoved serveru klientovi self.wfile.write("*** post ***".encode("utf-8"))
16. Implementace HTTP serveru
Ve skutečnosti bude implementace našeho testovacího serveru nepatrně komplikovanější, protože budeme potřebovat, aby se u metody POST získalo i tělo požadavku, které může obsahovat data poslaná klientem. To se dá provést relativně jednoduše – nejprve zjistíme délku obsahu (v bajtech) a posléze tento obsah načteme metodou rfile.read(), které se předá délka těla požadavku:
content_length = int(self.headers['Content-Length']) print("content length: {len}".format(len=content_length)) content = self.rfile.read(content_length)
Následně může server tato data zobrazit ve svém terminálu či do logovacího souboru, což je přesně to, co potřebujeme – získat nezpracovaný formát požadavku vytvořený knihovnou Requests.
Úplná implementace našeho HTTP serveru je založena na zdrojovém kódu, který byl poslán na https://gist.github.com/bradmontgomery/2219997, ovšem provedl jsem v něm několik úprav a oprav. Výsledek je použitelný s Pythonem 3.x:
#!/usr/bin/python3 # vim: set fileencoding=utf-8 # Original (slightly buggy) code: # see https://gist.github.com/bradmontgomery/2219997 import socket from http.server import BaseHTTPRequestHandler, HTTPServer hostName = "" hostPort = 8000 class SimpleServer(BaseHTTPRequestHandler): def do_GET(self): # priprava hlavicky odpovedi self.send_response(200) self.send_header('Content-type', 'text/plain') self.end_headers() # odpoved serveru klientovi self.wfile.write("*** get ***".encode("utf-8")) def do_POST(self): print("URI: {uri}".format(uri=self.path)) # precteni tela HTTP pozadavku content_length = int(self.headers['Content-Length']) print("content length: {len}".format(len=content_length)) content = self.rfile.read(content_length) print("content value: {content}".format(content=content)) # priprava hlavicky odpovedi self.send_response(200) self.send_header('Content-type', 'text/plain') self.end_headers() # odpoved serveru klientovi self.wfile.write("*** post ***".encode("utf-8")) simpleServer = HTTPServer((hostName, hostPort), SimpleServer) try: simpleServer.serve_forever() except KeyboardInterrupt: pass simpleServer.server_close()
17. Požadavek poslaný na lokálně běžící HTTP server
Výše popsaný HTTP server zavoláme celkem třikrát – jednou se použije metoda GET, podruhé metoda POST s předáním „formulářových dat“ a nakonec se opět použije metoda POST, ovšem tentokrát se data předají v těle požadavku s využitím formátu JSON:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests # adresa lokalne beziciho serveru URL = "http://localhost:8000" # poslani HTTP dotazu typu GET response = requests.get(URL) # vypis zakladnich informaci ziskanych z odpovedi print(response) print(response.status_code) print(response.ok) print(response.text) payload = { "klic": "hodnota", "answer": 42, "question": None, "correct": True} # poslani dat jako hodnot formulare response = requests.post(URL, data=payload) print(response.text) # poslani dat v tele dotazu response = requests.post(URL, json=payload) print(response.text)
Na konzoli, ze které spouštíme testovací skript, se vypíšou tyto údaje. První čtyři řádky platí pro první volání GET, další dva pro volání POST:
<Response [200]> 200 True *** get *** *** post *** *** post ***
Na konzoli serveru (ovšem nikoli na konzoli, kde spouštíme testovací skript!) by se měly vypsat následující řádky, z nichž je patrný jak formát požadavku typu GET, tak i formát požadavku typu POST při předávání údajů přes formulářová data a nakonec formát požadavku předaného v těle (JSON):
127.0.0.1 - - [03/Aug/2018 13:57:57] "GET / HTTP/1.1" 200 - URI: / content length: 35 content value: b'klic=hodnota&answer=42&correct=True' 127.0.0.1 - - [03/Aug/2018 13:57:57] "POST / HTTP/1.1" 200 - URI: / content length: 68 content value: b'{"klic": "hodnota", "answer": 42, "question": null, "correct": true}' 127.0.0.1 - - [03/Aug/2018 13:57:57] "POST / HTTP/1.1" 200 -
$ curl -v localhost:8000 * Rebuilt URL to: localhost:8000/ * Trying ::1... * TCP_NODELAY set * connect to ::1 port 8000 failed: Connection refused * Trying 127.0.0.1... * TCP_NODELAY set * Connected to localhost (127.0.0.1) port 8000 (#0) > GET / HTTP/1.1 > Host: localhost:8000 > User-Agent: curl/7.55.1 > Accept: */* > * HTTP 1.0, assume close after body < HTTP/1.0 200 OK < Server: BaseHTTP/0.6 Python/3.6.6 < Date: Mon, 06 Jul 2020 10:05:25 GMT < Content-type: text/plain < * Closing connection 0
$ telnet localhost 8000 Trying ::1... telnet: connect to address ::1: Connection refused Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. GET / *** get *** Connection closed by foreign host.
18. Repositář s demonstračními příklady
Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do nového Git repositáře, který je dostupný na adrese https://github.com/tisnik/testing-in-python. V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, dnes má přibližně několik desítek kilobajtů), můžete namísto toho použít odkazy na jednotlivé demonstrační příklady a jejich části, které naleznete v následující tabulce:
19. Předchozí články s tématem testování (nejenom) v Pythonu
Tématem testování jsme se již na stránkách Rootu několikrát zabývali. Jedná se mj. o následující články:
- Requests: HTTP for Humans (dokumentace)
http://docs.python-requests.org/en/master/ - Requests: Introduction
http://docs.python-requests.org/en/latest/user/intro/ - Requests na GitHubu
https://github.com/requests/requests - Requests (software) na Wikipedii
https://en.wikipedia.org/wiki/Requests_%28software%29 - Pip (dokumentace)
https://pip.pypa.io/en/stable/ - 20 Python libraries you can’t live without
https://pythontips.com/2013/07/30/20-python-libraries-you-cant-live-without/ - What are the top 10 most useful and influential Python libraries and frameworks?
https://www.quora.com/What-are-the-top-10-most-useful-and-influential-Python-libraries-and-frameworks - Python: useful modules
https://wiki.python.org/moin/UsefulModules - Top 15 most popular Python libraries
https://keyua.org/blog/most-popular-python-libraries/ - Hypertext Transfer Protocol
https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol - List of HTTP header fields
https://en.wikipedia.org/wiki/List_of_HTTP_header_fields - List of HTTP status codes
https://en.wikipedia.org/wiki/List_of_HTTP_status_codes - Python requests deep dive
https://medium.com/@anthonypjshaw/python-requests-deep-dive-a0a5c5c1e093 - The awesome requests module
https://www.pythonforbeginners.com/requests/the-awesome-requests-module - Send HTTP Requests in Python
https://code-maven.com/http-requests-in-python - Introducing JSON
http://json.org/ - Použití Pythonu pro tvorbu testů: od jednotkových testů až po testy UI
https://www.root.cz/clanky/pouziti-pythonu-pro-tvorbu-testu-od-jednotkovych-testu-az-po-testy-ui/ - Použití Pythonu pro tvorbu testů: použití třídy Mock z knihovny unittest.mock
https://www.root.cz/clanky/pouziti-pythonu-pro-tvorbu-testu-pouziti-tridy-mock-z-knihovny-unittest-mock/ - Použití nástroje pytest pro tvorbu jednotkových testů a benchmarků
https://www.root.cz/clanky/pouziti-nastroje-pytest-pro-tvorbu-jednotkovych-testu-a-benchmarku/ - Nástroj pytest a jednotkové testy: fixtures, výjimky, parametrizace testů
https://www.root.cz/clanky/nastroj-pytest-a-jednotkove-testy-fixtures-vyjimky-parametrizace-testu/ - Nástroj pytest a jednotkové testy: životní cyklus testů, užitečné tipy a triky
https://www.root.cz/clanky/nastroj-pytest-a-jednotkove-testy-zivotni-cyklus-testu-uzitecne-tipy-a-triky/ - Struktura projektů s jednotkovými testy, využití Travis CI
https://www.root.cz/clanky/struktura-projektu-s-jednotkovymi-testy-vyuziti-travis-ci/ - Omezení stavového prostoru testovaných funkcí a metod
https://www.root.cz/clanky/omezeni-stavoveho-prostoru-testovanych-funkci-a-metod/ - Testování aplikací s využitím nástroje Hypothesis
https://www.root.cz/clanky/testovani-aplikaci-s-vyuzitim-nastroje-hypothesis/ - Testování aplikací s využitím nástroje Hypothesis (dokončení)
https://www.root.cz/clanky/testovani-aplikaci-s-vyuzitim-nastroje-hypothesis-dokonceni/ - Behavior-driven development v Pythonu s využitím knihovny Behave
https://www.root.cz/clanky/behavior-driven-development-v-pythonu-s-vyuzitim-knihovny-behave/ - Behavior-driven development v Pythonu s využitím knihovny Behave (druhá část)
https://www.root.cz/clanky/behavior-driven-development-v-pythonu-s-vyuzitim-knihovny-behave-druha-cast/ - Behavior-driven development v Pythonu s využitím knihovny Behave (závěrečná část)
https://www.root.cz/clanky/behavior-driven-development-v-pythonu-s-vyuzitim-knihovny-behave-zaverecna-cast/ - Validace datových struktur v Pythonu pomocí knihoven Schemagic a Schema
https://www.root.cz/clanky/validace-datovych-struktur-v-pythonu-pomoci-knihoven-schemagic-a-schema/ - Validace datových struktur v Pythonu (2. část)
https://www.root.cz/clanky/validace-datovych-struktur-v-pythonu-2-cast/ - Validace datových struktur v Pythonu (dokončení)
https://www.root.cz/clanky/validace-datovych-struktur-v-pythonu-dokonceni/ - Univerzální testovací nástroj Robot Framework
https://www.root.cz/clanky/univerzalni-testovaci-nastroj-robot-framework/ - Univerzální testovací nástroj Robot Framework a BDD testy
https://www.root.cz/clanky/univerzalni-testovaci-nastroj-robot-framework-a-bdd-testy/ - Úvod do problematiky fuzzingu a fuzz testování
https://www.root.cz/clanky/uvod-do-problematiky-fuzzingu-a-fuzz-testovani/ - Úvod do problematiky fuzzingu a fuzz testování – složení vlastního fuzzeru
https://www.root.cz/clanky/uvod-do-problematiky-fuzzingu-a-fuzz-testovani-slozeni-vlastniho-fuzzeru/ - Knihovny a moduly usnadňující testování aplikací naprogramovaných v jazyce Clojure
https://www.root.cz/clanky/knihovny-a-moduly-usnadnujici-testovani-aplikaci-naprogramovanych-v-jazyce-clojure/ - Validace dat s využitím knihovny spec v Clojure 1.9.0
https://www.root.cz/clanky/validace-dat-s-vyuzitim-knihovny-spec-v-clojure-1–9–0/ - Testování aplikací naprogramovaných v jazyce Go
https://www.root.cz/clanky/testovani-aplikaci-naprogramovanych-v-jazyce-go/ - Knihovny určené pro tvorbu testů v programovacím jazyce Go
https://www.root.cz/clanky/knihovny-urcene-pro-tvorbu-testu-v-programovacim-jazyce-go/ - Testování aplikací psaných v Go s využitím knihoven Goblin a Frisby
https://www.root.cz/clanky/testovani-aplikaci-psanych-v-go-s-vyuzitim-knihoven-goblin-a-frisby/ - Testování Go aplikací s využitím knihovny GΩmega a frameworku Ginkgo
https://www.root.cz/clanky/testovani-go-aplikaci-s-vyuzitim-knihovny-gomega-mega-a-frameworku-ginkgo/ - Tvorba BDD testů s využitím jazyka Go a nástroje godog
https://www.root.cz/clanky/tvorba-bdd-testu-s-vyuzitim-jazyka-go-a-nastroje-godog/ - Použití Go pro automatizaci práce s aplikacemi s interaktivním příkazovým řádkem
https://www.root.cz/clanky/pouziti-go-pro-automatizaci-prace-s-aplikacemi-s-interaktivnim-prikazovym-radkem/ - Použití Go pro automatizaci práce s aplikacemi s interaktivním příkazovým řádkem (dokončení)
https://www.root.cz/clanky/pouziti-go-pro-automatizaci-prace-s-aplikacemi-s-interaktivnim-prikazovym-radkem-dokonceni/ - Použití jazyka Gherkin při tvorbě testovacích scénářů pro aplikace psané v Clojure
https://www.root.cz/clanky/pouziti-jazyka-gherkin-pri-tvorbe-testovacich-scenaru-pro-aplikace-psane-v-nbsp-clojure/ - Použití jazyka Gherkin při tvorbě testovacích scénářů pro aplikace psané v Clojure (2)
https://www.root.cz/clanky/pouziti-jazyka-gherkin-pri-tvorbe-testovacich-scenaru-pro-aplikace-psane-v-nbsp-clojure-2/
20. Odkazy na Internetu
- Writing tests for RESTful APIs in Python using requests – part 1: basic tests
https://www.ontestautomation.com/writing-tests-for-restful-apis-in-python-using-requests-part-1-basic-tests/ - Step by Step Rest API Testing using Python + Pytest + Allure
https://www.udemy.com/course/api-testing-python/ - Prime formulas and polynomial functions
https://en.wikipedia.org/wiki/Formula_for_primes#Prime_formulas_and_polynomial_functions - Prime-Generating Polynomial
https://mathworld.wolfram.com/Prime-GeneratingPolynomial.html - Hoare logic
https://en.wikipedia.org/wiki/Hoare_logic - Goto Fail, Heartbleed, and Unit Testing Culture
https://martinfowler.com/articles/testing-culture.html - PEP-484
https://www.python.org/dev/peps/pep-0484/ - In-depth: Functional programming in C++
https://www.gamasutra.com/view/news/169296/Indepth_Functional_programming_in_C.php - mypy
http://www.mypy-lang.org/ - Welcome to Mypy documentation!
https://mypy.readthedocs.io/en/latest/index.html - mypy na GitHubu
https://github.com/python/mypy - mypy 0.770 na PyPi
https://pypi.org/project/mypy/ - Extensions for mypy (separated out from mypy/extensions)
https://github.com/python/mypy_extensions - The Mypy Blog
https://mypy-lang.blogspot.com/2020/03/mypy-0770-released.html - Our journey to type checking 4 million lines of Python
https://dropbox.tech/application/our-journey-to-type-checking-4-million-lines-of-python - Type-Checking Python Programs With Type Hints and mypy
https://www.youtube.com/watch?v=2×WhaALHTvU - Refactoring to Immutability – Kevlin Henney
https://www.youtube.com/watch?v=APUCMSPiNh4 - Bernat Gabor – Type hinting (and mypy) – PyCon 2019
https://www.youtube.com/watch?v=hTrjTAPnA_k - Stanford Seminar – Optional Static Typing for Python
https://www.youtube.com/watch?v=GiZKuyLKvAA - mypy Getting to Four Million Lines of Typed Python – Michael Sullivan
https://www.youtube.com/watch?v=FT_WHV4-QcU - Shebang
https://en.wikipedia.org/wiki/Shebang_(Unix) - pytest 5.4.2 na PyPi
https://pypi.org/project/pytest/ - Hillel Wayne – Beyond Unit Tests: Taking Your Testing to the Next Level – PyCon 2018
https://www.youtube.com/watch?v=MYucYon2-lk - Awesome Python – testing
https://github.com/vinta/awesome-python#testing - pytest Plugins Compatibility
http://plugincompat.herokuapp.com/ - Selenium (pro Python)
https://pypi.org/project/selenium/ - Getting Started With Testing in Python
https://realpython.com/python-testing/ - unittest.mock — mock object library
https://docs.python.org/3.5/library/unittest.mock.html - mock 2.0.0
https://pypi.python.org/pypi/mock - An Introduction to Mocking in Python
https://www.toptal.com/python/an-introduction-to-mocking-in-python - Mock – Mocking and Testing Library
http://mock.readthedocs.io/en/stable/ - Python Mocking 101: Fake It Before You Make It
https://blog.fugue.co/2016–02–11-python-mocking-101.html - Nauč se Python! – Testování
http://naucse.python.cz/lessons/intro/testing/ - Flexmock (dokumentace)
https://flexmock.readthedocs.io/en/latest/ - Test Fixture (Wikipedia)
https://en.wikipedia.org/wiki/Test_fixture - Mock object (Wikipedia)
https://en.wikipedia.org/wiki/Mock_object - Extrémní programování
https://cs.wikipedia.org/wiki/Extr%C3%A9mn%C3%AD_programov%C3%A1n%C3%AD - Programování řízené testy
https://cs.wikipedia.org/wiki/Programov%C3%A1n%C3%AD_%C5%99%C3%ADzen%C3%A9_testy - Pip (dokumentace)
https://pip.pypa.io/en/stable/ - Tox
https://tox.readthedocs.io/en/latest/ - pytest: helps you write better programs
https://docs.pytest.org/en/latest/ - doctest — Test interactive Python examples
https://docs.python.org/dev/library/doctest.html#module-doctest - unittest — Unit testing framework
https://docs.python.org/dev/library/unittest.html - Python namespaces
https://bytebaker.com/2008/07/30/python-namespaces/ - Namespaces and Scopes
https://www.python-course.eu/namespaces.php - Stránka projektu Robot Framework
https://robotframework.org/ - GitHub repositář Robot Frameworku
https://github.com/robotframework/robotframework - Robot Framework (Wikipedia)
https://en.wikipedia.org/wiki/Robot_Framework - Tutoriál Robot Frameworku
http://www.robotframeworktutorial.com/ - Robot Framework Documentation
https://robotframework.org/robotframework/ - Robot Framework Introduction
https://blog.testproject.io/2016/11/22/robot-framework-introduction/ - robotframework 3.1.2 na PyPi
https://pypi.org/project/robotframework/ - Robot Framework demo (GitHub)
https://github.com/robotframework/RobotDemo - Robot Framework web testing demo using SeleniumLibrary
https://github.com/robotframework/WebDemo - Robot Framework for Mobile Test Automation Demo
https://www.youtube.com/watch?v=06LsU08slP8 - Gherkin
https://cucumber.io/docs/gherkin/ - Selenium
https://selenium.dev/ - SeleniumLibrary
https://robotframework.org/ - The Practical Test Pyramid
https://martinfowler.com/articles/practical-test-pyramid.html - Acceptance Tests and the Testing Pyramid
http://www.blog.acceptancetestdrivendevelopment.com/acceptance-tests-and-the-testing-pyramid/ - Tab-separated values
https://en.wikipedia.org/wiki/Tab-separated_values - A quick guide about Python implementations
https://blog.rmotr.com/a-quick-guide-about-python-implementations-aa224109f321 - radamsa
https://gitlab.com/akihe/radamsa - Fuzzing (Wikipedia)
https://en.wikipedia.org/wiki/Fuzzing - american fuzzy lop
http://lcamtuf.coredump.cx/afl/ - Fuzzing: the new unit testing
https://go-talks.appspot.com/github.com/dvyukov/go-fuzz/slides/fuzzing.slide#1 - Corpus for github.com/dvyukov/go-fuzz examples
https://github.com/dvyukov/go-fuzz-corpus - AFL – QuickStartGuide.txt
https://github.com/google/AFL/blob/master/docs/QuickStartGuide.txt - Introduction to Fuzzing in Python with AFL
https://alexgaynor.net/2015/apr/13/introduction-to-fuzzing-in-python-with-afl/ - Writing a Simple Fuzzer in Python
https://jmcph4.github.io/2018/01/19/writing-a-simple-fuzzer-in-python/ - How to Fuzz Go Code with go-fuzz (Continuously)
https://fuzzit.dev/2019/10/02/how-to-fuzz-go-code-with-go-fuzz-continuously/ - Golang Fuzzing: A go-fuzz Tutorial and Example
http://networkbit.ch/golang-fuzzing/ - Fuzzing Python Modules
https://stackoverflow.com/questions/20749026/fuzzing-python-modules - 0×3 Python Tutorial: Fuzzer
http://www.primalsecurity.net/0×3-python-tutorial-fuzzer/ - fuzzing na PyPi
https://pypi.org/project/fuzzing/ - Fuzzing 0.3.2 documentation
https://fuzzing.readthedocs.io/en/latest/ - Randomized testing for Go
https://github.com/dvyukov/go-fuzz - HTTP/2 fuzzer written in Golang
https://github.com/c0nrad/http2fuzz - Ffuf (Fuzz Faster U Fool) – An Open Source Fast Web Fuzzing Tool
https://hacknews.co/hacking-tools/20191208/ffuf-fuzz-faster-u-fool-an-open-source-fast-web-fuzzing-tool.html - Continuous Fuzzing Made Simple
https://fuzzit.dev/ - Halt and Catch Fire
https://en.wikipedia.org/wiki/Halt_and_Catch_Fire#Intel_x86 - Random testing
https://en.wikipedia.org/wiki/Random_testing - Monkey testing
https://en.wikipedia.org/wiki/Monkey_testing - Fuzzing for Software Security Testing and Quality Assurance, Second Edition
https://books.google.at/books?id=tKN5DwAAQBAJ&pg=PR15&lpg=PR15&q=%22I+settled+on+the+term+fuzz%22&redir_esc=y&hl=de#v=onepage&q=%22I%20settled%20on%20the%20term%20fuzz%22&f=false - libFuzzer – a library for coverage-guided fuzz testing
https://llvm.org/docs/LibFuzzer.html - fuzzy-swagger na PyPi
https://pypi.org/project/fuzzy-swagger/ - fuzzy-swagger na GitHubu
https://github.com/namuan/fuzzy-swagger - Fuzz testing tools for Python
https://wiki.python.org/moin/PythonTestingToolsTaxonomy#Fuzz_Testing_Tools - A curated list of awesome Go frameworks, libraries and software
https://github.com/avelino/awesome-go - gofuzz: a library for populating go objects with random values
https://github.com/google/gofuzz - tavor: A generic fuzzing and delta-debugging framework
https://github.com/zimmski/tavor - hypothesis na GitHubu
https://github.com/HypothesisWorks/hypothesis - Hypothesis: Test faster, fix more
https://hypothesis.works/ - Hypothesis
https://hypothesis.works/articles/intro/ - What is Hypothesis?
https://hypothesis.works/articles/what-is-hypothesis/ - What is Property Based Testing?
https://hypothesis.works/articles/what-is-property-based-testing/ - Databáze CVE
https://www.cvedetails.com/ - Fuzz test Python modules with libFuzzer
https://github.com/eerimoq/pyfuzzer - Taof – The art of fuzzing
https://sourceforge.net/projects/taof/ - JQF + Zest: Coverage-guided semantic fuzzing for Java
https://github.com/rohanpadhye/jqf - http2fuzz
https://github.com/c0nrad/http2fuzz - Demystifying hypothesis testing with simple Python examples
https://towardsdatascience.com/demystifying-hypothesis-testing-with-simple-python-examples-4997ad3c5294 - Testování
http://voho.eu/wiki/testovani/ - Unit testing (Wikipedia.en)
https://en.wikipedia.org/wiki/Unit_testing - Unit testing (Wikipedia.cz)
https://cs.wikipedia.org/wiki/Unit_testing - Unit Test vs Integration Test
https://www.youtube.com/watch?v=0GypdsJulKE - TestDouble
https://martinfowler.com/bliki/TestDouble.html - Test Double
http://xunitpatterns.com/Test%20Double.html - Test-driven development (Wikipedia)
https://en.wikipedia.org/wiki/Test-driven_development - Acceptance test–driven development
https://en.wikipedia.org/wiki/Acceptance_test%E2%80%93driven_development - Gauge
https://gauge.org/ - Gauge (software)
https://en.wikipedia.org/wiki/Gauge_(software) - PYPL PopularitY of Programming Language
https://pypl.github.io/PYPL.html - Testing is Good. Pyramids are Bad. Ice Cream Cones are the Worst
https://medium.com/@fistsOfReason/testing-is-good-pyramids-are-bad-ice-cream-cones-are-the-worst-ad94b9b2f05f - Články a zprávičky věnující se Pythonu
https://www.root.cz/n/python/ - PythonTestingToolsTaxonomy
https://wiki.python.org/moin/PythonTestingToolsTaxonomy - Top 6 BEST Python Testing Frameworks [Updated 2020 List]
https://www.softwaretestinghelp.com/python-testing-frameworks/ - pytest-print 0.1.3
https://pypi.org/project/pytest-print/ - pytest fixtures: explicit, modular, scalable
https://docs.pytest.org/en/latest/fixture.html - PyTest Tutorial: What is, Install, Fixture, Assertions
https://www.guru99.com/pytest-tutorial.html - Pytest – Fixtures
https://www.tutorialspoint.com/pytest/pytest_fixtures.htm - Marking test functions with attributes
https://docs.pytest.org/en/latest/mark.html - pytest-print
https://pytest-print.readthedocs.io/en/latest/ - Continuous integration
https://en.wikipedia.org/wiki/Continuous_integration - Travis CI
https://travis-ci.org/ - Mutation testing
https://en.wikipedia.org/wiki/Mutation_testing - Články o Hypothesis
https://news.ycombinator.com/from?site=hypothesis.works - Testovací případ
https://cs.wikipedia.org/wiki/Testovac%C3%AD_p%C5%99%C3%ADpad - Most testing is ineffective
https://hypothesis.works/