Obsah
1. Testování webových aplikací s REST API z Pythonu (2)
2. Přečtení nezpracovaného těla odpovědi při práci s binárními daty
3. První demonstrační příklad – přečtení bloku binárních dat z odpovědi serveru
4. Získání a uložení rastrového obrázku typu image/png a image/jpeg
5. Využití hlavičky accept posílané v požadavku serveru pro určení formátu dat
6. CRUD operace a jejich obdoba v HTTP metodách
7. Požadavek využívající metodu PATCH
8. Požadavek využívající metodu PUT
9. Požadavek využívající metodu OPTIONS
11. Poslání cookies v GET požadavku
12. Objekt typu Session a jeho využití pro uložení stavu mezi dotazy
13. Příklad na použití objektu typu Session
14. Úprava testovacího HTTP serveru takovým způsobem, aby vypisoval hlavičky posílané klientem
15. Nová podoba testovacího HTTP serveru
16. Skript pro volání testovacího HTTP serveru s hlavičkou Set-Cookie
17. Spuštění skriptu, ukázka celého tvaru požadavku a formát odpovědi serveru
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 (2)
V dnešním článku si ukážeme některé další možnosti nabízené knihovnou Requests, s jejímž základním použitím jsme se seznámili v článku předchozím. Nejprve si řekneme, jak lze přečíst binární data poslaná serverem v odpovědi (typicky se bude jednat o rastrové obrázky, audio data, archivy atd.), použijeme HTTP metody pojmenované PATCH, PUT a OPTIONS a dále se seznámíme s konceptem takzvaných cookies a ukážeme si, jakým způsobem je možné využít objekt typu Session, díky němuž je možné s cookies (a obecně se „sezeními“) velmi snadno a rychle pracovat – právě zde se ostatně ukáže snadnost použití knihovny Requests v porovnání s jinými knihovnami a nástroji, které mnohdy nenabízejí všechny operace, popř. je sice nabízejí, ale na odlišné „úrovni abstrakce“ (buď zbytečně nízkoúrovňově, nebo naopak některé možnosti zabalují do abstraktních operací). A session budou užitečné i při psaní testů pro REST API, což si prakticky ukážeme příště.
2. Přečtení nezpracovaného těla odpovědi při práci s binárními daty
Při vývoji některých aplikací využívajících REST API, popř. při přímém přístupu k prostředkům (obrázkům, animacím, …) nabízeným na HTTP serverech se někdy setkáme s nutností zpracování binárních dat vrácených serverem s využitím protokolu HTTP nebo HTTPS. V takovém případě je možné (a také vhodné) obejít většinu funkcí nabízených knihovnou Requests a přečíst binární data vlastními prostředky – bajt po bajtu nebo blok po bloku. Ve skutečnosti je to relativně jednoduché. Nejdříve musíme zjistit typ dat (pro jistotu) a taktéž jejich délku, kterou by měl HTTP server poslat v hlavičce (pozor – ne vždy se tak děje korektně!):
# přečtení hlaviček headers = response.headers # výpis typu internetoveho media print("Typ dat:", headers.get("content-type")) # výpis delky dat predanych v tele print("Delka dat:", headers.get("content-length"))
Dále již jen využijeme zjištěnou délku a použijeme metodu nazvanou response.raw.read(délka), která je určena pro postupné přečtení binárních dat z těla odpovědi. Zde pro jednoduchost budeme číst data po bajtu, i když výhodnější by bylo čtení po delších blocích:
# délka dat předaných v těle odpovědi length = int(headers.get("content-length")) # přečtení těla odpovědi bajt po bajtu for i in range(length): byte = response.raw.read(1) print(hex(byte[0]))
3. První demonstrační příklad – přečtení bloku binárních dat z odpovědi serveru
V dnešním prvním demonstračním příkladu, jehož úplný zdrojový kód je dostupný na GitHubu https://github.com/tisnik/testing-in-python/tree/master/requests/11_get_binary_data.py, využijeme službu dostupnou na adrese https://httpbin.org/bytes/_počet_bajtů_, která v těle odpovědi vrátí požadovaný počet bajtů (ty budou mít náhodnou, resp. přesněji řečeno pseudonáhodnou hodnotu!). Konkrétně budeme vyžadovat sto náhodných bajtů, takže požadavek bude směřován na adresu https://httpbin.org/bytes/100:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests # adresa s testovací REST API službou URL = "https://httpbin.org/bytes/100" # poslání HTTP dotazu typu GET response = requests.get(URL, stream=True) # přečtení hlaviček headers = response.headers # výpis typu internetového media print("Typ dat:", headers.get("content-type")) # výpis délky dat předaných v těle odpovědi print("Delka dat:", headers.get("content-length")) length = int(headers.get("content-length")) # přečtení těla odpovědi bajt po bajtu for i in range(length): byte = response.raw.read(1) print(hex(byte[0]))
Po spuštění příkladu by měl začátek zpráv vypisovaných na terminál začínat takto:
$ ./11_get_binary_data.py Typ dat: application/octet-stream Delka dat: 100
Následuje výpis hexadecimálních hodnot stovky bajtů, které byly poslány serverem v odpovědi:
0x20 0xc1 0x34 0xe6 0x6b 0x0 0x35 0x12 0xf4 0xae 0xed 0xc7 0xe9 0xc5 0x86 0xba 0x94 0x1f 0x4e ... ... ...
Podobně můžeme postupovat i z příkazové řádky za použití nástroje curl:
$ curl https://httpbin.org/bytes/100 2>/dev/null | od -t x1 0000000 72 9d 36 d3 67 50 f3 6b 49 e9 13 f0 37 94 81 11 0000020 4f 02 dc 28 dd 00 ce 35 07 29 e8 94 42 b7 c6 72 0000040 f7 3b ca 72 68 57 19 01 6f 1a a6 d8 aa 3d ed 03 0000060 7d 23 c8 c7 f3 b7 37 7b 21 a9 8f 60 e2 4e e8 9c 0000100 e0 63 2b b0 4f 0f 4f d9 f4 0b 38 c6 66 43 fa 86 0000120 c9 e4 79 97 3c ce e4 35 b0 1b 3c 9f 0d 87 f8 0b 0000140 e8 25 ce 39 0000144
4. Získání a uložení rastrového obrázku typu image/png a image/jpeg
Ve druhém demonstračním příkladu (https://github.com/tisnik/testing-in-python/tree/master/requests/12_binary_data_png_image.py) si ukážeme způsob základního zpracování rastrového obrázku typu PNG předaného v binární podobě po poslání HTTP metody GET na adresu https://httpbin.org/image/png. Obrázek je možné uložit do (binárního) souboru po blocích, jejichž délku jsme nastavili na 128 bajtů (lze samozřejmě použít i kratší či naopak delší bloky). Namísto metody response.raw.read() použijeme alternativní přístup přes metodu response.iter_content(), která navíc správně vyřeší i délku posledního bloku (délka tohoto bloku samozřejmě může být menší než 128 bajtů):
with open("test1.png", 'wb') as fout: for block in response.iter_content(chunk_size=128): fout.write(block)
Úplný zdrojový kód tohoto příkladu vypadá následovně:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests # adresa s testovací REST API službou URL = "https://httpbin.org/image/png" # poslání HTTP dotazu typu GET response = requests.get(URL) # přečtení hlaviček headers = response.headers # výpis typu internetového média print("Typ dat:", headers.get("content-type")) # výpis délky dat předaných v těle odpovědi print("Delka dat:", headers.get("content-length")) print(response.raw) with open("test1.png", 'wb') as fout: for block in response.iter_content(chunk_size=128): fout.write(block)
Dtto, ale za použití nástroje curl:
$ curl -o test1.png https://httpbin.org/image/png % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 8090 100 8090 0 0 8090 0 0:00:01 --:--:-- 0:00:01 10932 $ file test1.png test1.png: PNG image data, 100 x 100, 8-bit/color RGB, non-interlaced
Zcela stejným způsobem můžeme přečíst a uložit rastrový obrázek typu JPEG, který je testovací REST API službou vrácen při poslání HTTP metody GET na adresu https://httpbin.org/image/jpeg:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests # adresa s testovací REST API službou URL = "https://httpbin.org/image/jpeg" # poslání HTTP dotazu typu GET response = requests.get(URL) # přečtení hlaviček headers = response.headers # výpis typu internetového média print("Typ dat:", headers.get("content-type")) # výpis délky dat předaných v těle odpovědi print("Delka dat:", headers.get("content-length")) print(response.raw) with open("test1.jpg", 'wb') as fout: for block in response.iter_content(chunk_size=128): fout.write(block)
Dtto, ale za použití nástroje curl:
$ curl -o test1.jpeg https://httpbin.org/image/jpeg % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 35588 100 35588 0 0 35588 0 0:00:01 0:00:01 --:--:-- 31946 $ file test1.jpeg test1.jpeg: JPEG image data, JFIF standard 1.01, resolution (DPCM), density 28x28, segment length 16, comment: "Edited by Paul Sherman for WPClipart, Public Domain", baseline, precision 8, 239x178, frames 3
5. Využití hlavičky accept posílané v požadavku serveru pro určení formátu dat
Testovací REST API služba, kterou používáme ve většině dnes popisovaných demonstračních příkladů, nabízí i možnost výběru formátu rastrových dat. Výběr se přitom provádí nastavením hlavičky se jménem accept v požadavku (request) poslaném na server. Na základě obsahu (tedy hodnoty) této hlavičky server připraví data v požadovaném formátu a pošle je (v binární podobě) zpět klientovi. Příslušná adresa, na kterou je nutné požadavek s hlavičkou accept poslat, je https://httpbin.org/image.
Jedním z podporovaných formátů je i „image/png“, takže další demonstrační příklad přečte obrázek ve formátu PNG a uloží ho do souboru „test.png“:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests # adresa s testovací REST API službou URL = "https://httpbin.org/image" # hlavička posílaná v dotazu headers = {'accept': 'image/png'} # poslání HTTP dotazu typu GET response = requests.get(URL, headers=headers) # přečtení hlaviček headers = response.headers # výpis typu internetového média print("Typ dat:", headers.get("content-type")) # výpis délky dat předaných v těle odpovědi print("Delka dat:", headers.get("content-length")) print(response.raw) with open("test2.png", 'wb') as fout: for block in response.iter_content(chunk_size=128): fout.write(block)
Dalším podporovaným formátem dat testovací služby dostupné na adrese https://httpbin.org/image je image/jpeg, který pochopitelně vrací rastrový obrázek uložený ve formátu JPEG:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests # adresa s testovací REST API službou URL = "https://httpbin.org/image" # hlavička posílaná v dotazu headers = {'accept': 'image/jpeg'} # poslání HTTP dotazu typu GET response = requests.get(URL, headers=headers) # přečtení hlaviček headers = response.headers # výpis typu internetového média print("Typ dat:", headers.get("content-type")) # výpis délky dat předaných v těle odpovědi print("Delka dat:", headers.get("content-length")) print(response.raw) with open("test2.jpg", 'wb') as fout: for block in response.iter_content(chunk_size=128): fout.write(block)
Alternativní využití příkazového řádku a nástroje curl vyžaduje použití přepínače -H:
$ curl https://httpbin.org/image {"message": "Client did not request a supported media type.", "accept": ["image/webp", "image/svg+xml", "image/jpeg", "image/png", "image/*"]}
$ curl -H accept:image/jpeg https://httpbin.org/image -o test2.jpeg % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 35588 100 35588 0 0 35588 0 0:00:01 --:--:-- 0:00:01 36613 $ file test2.jpeg test2.jpeg: JPEG image data, JFIF standard 1.01, resolution (DPCM), density 28x28, segment length 16, comment: "Edited by Paul Sherman for WPClipart, Public Domain", baseline, precision 8, 239x178, frames 3
$ curl -H accept:image/png https://httpbin.org/image -o test2.png % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 8090 100 8090 0 0 8090 0 0:00:01 --:--:-- 0:00:01 8968 $ file test2.png test2.png: PNG image data, 100 x 100, 8-bit/color RGB, non-interlaced
6. CRUD operace a jejich obdoba v HTTP metodách
Již minule jsme si popsali dvě základní HTTP metody nazvané GET a POST. Kromě těchto metod však existují i metody další. Některé z nich (například metoda OPTIONS) hrají spíše pomocnou roli související se samotným síťovým protokolem (popřípadě s CORSem), ovšem metody pojmenované PUT a DELETE a částečně i metoda PATCH hrají důležitou roli v takzvaných CRUD operacích. V tabulce vypsané pod tímto odstavcem jsou vypsány všechny čtyři základní operace typu CRUD neboli Create, Read, Update a Delete používanými při práci s daty uloženými v nějakém perzistentním úložišti, například v databázi umístěné na serveru (je vcelku jedno, o jakou databázi se jedná, zda o relační, dokumentovou atd.). Tyto operace mají své sémantické protějšky ve čtyřech HTTP metodách, které nalezneme ve druhém sloupci:
Operace | HTTP metoda |
---|---|
Create | POST |
Read (Retrieve) | GET |
Update (Modify) | PUT |
Delete (Destroy) | DELETE |
7. Požadavek využívající metodu PATCH
V dalším demonstračním skriptu, jehož úplný zdrojový kód naleznete na adrese https://github.com/tisnik/testing-in-python/tree/master/requests/16_patch_method.py, je ukázán základní způsob použití HTTP metody nazvané PATCH. Tato metoda se prozatím nepoužívá příliš často, a to ani v některých REST API službách, ovšem její sémantika předurčuje tuto metodu použít ve chvíli, kdy je nutné modifikovat nějaký již existující zdroj (resource) uložený na serveru (popř. v relační databázi apod.). Pokud by se například jednalo o úpravu jediného atributu, bude požadavek používající metodu PATCH kratší a provedený rychleji, než úplný požadavek založený na metodě PUT (POST má naproti tomu dosti odlišnou sémantiku – slouží k vytvoření nového zdroje):
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests import json # adresa s testovací REST API službou URL = "https://httpbin.org/patch" # hlavička posílaná v dotazu headers = {'accept': 'application/json'} # poslání HTTP dotazu typu PATCH response = requests.patch(URL, headers=headers) # přečtení hlaviček odpovědi headers = response.headers # výpis všech hlaviček odpovědi print("Headers:") for header_name, header_value in headers.items(): print("{:40s} {}".format(header_name, header_value)) print("-" * 60) print("Content:") # zpracování odpovědi, která přišla ve formátu JSON data = response.json() print(json.dumps(data, indent=4, sort_keys=True))
Ve skriptu je metoda PATCH použita pro přístup na adresu https://httpbin.org/patch sloužící k jejímu základnímu otestování. Odpověď vrácená serverem by měla vypadat přibližně následovně (vypsány jsou napřed hlavičky a poté i tělo odpovědi ve formátu JSON):
Headers: Connection keep-alive Server gunicorn/19.9.0 Date Wed, 08 Aug 2018 15:25:28 GMT Content-Type application/json Content-Length 373 Access-Control-Allow-Origin * Access-Control-Allow-Credentials true Via 1.1 vegur ------------------------------------------------------------ Content: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "application/json", "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": "https://httpbin.org/patch" }
Využití nástroje curl, napřed bez specifikace metody (použije se GET):
$ curl -v http://httpbin.org/patch * Trying 3.220.112.94... * TCP_NODELAY set * Connected to httpbin.org (3.220.112.94) port 80 (#0) > GET /patch HTTP/1.1 > Host: httpbin.org > User-Agent: curl/7.55.1 > Accept: */* > < HTTP/1.1 405 METHOD NOT ALLOWED < Date: Tue, 07 Jul 2020 17:22:04 GMT < Content-Type: text/html < Content-Length: 178 < Connection: keep-alive < Server: gunicorn/19.9.0 < Allow: PATCH, OPTIONS < 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
Explicitní specifikace HTTP metody PATCH:
$ curl -v -X PATCH http://httpbin.org/patch * Trying 54.236.246.173... * TCP_NODELAY set * Connected to httpbin.org (54.236.246.173) port 80 (#0) > PATCH /patch HTTP/1.1 > Host: httpbin.org > User-Agent: curl/7.55.1 > Accept: */* > < HTTP/1.1 200 OK < Date: Tue, 07 Jul 2020 17:23:15 GMT < Content-Type: application/json < Content-Length: 317 < 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-5f04af83-057eaaea85113b26e15ab29a" }, "json": null, "origin": "37.48.9.246", "url": "http://httpbin.org/patch" } * Connection #0 to host httpbin.org left intact
8. Požadavek využívající metodu PUT
Požadavek používající HTTP metodu PUT vypadá velmi podobně jako požadavek POST popsaný minule. Liší se však sémantika (přibližně řečeno logický význam) těchto metod, protože zatímco POST typicky slouží k založení nového zdroje (resource) na serveru, je PUT použit k přepisu již existujících údajů. Rozdíl je v některých případech patrný i při pohledu na URL (adresu), protože u metody PUT by se mělo odkazovat přímo na konkrétní resource, kdežto u metody POST se odkazuje spíše na obecnější funkci (můžeme dokonce říci, že na konstruktor), která nový resource vytvoří a vrátí jeho identifikátor v odpovědi serveru. Podívejme se nyní v rychlosti na způsob použití metody PUT, kde opět použijeme testovací službu https://httpbin.org/, konkrétně URL/adresu https://httpbin.org/put:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests import json # adresa s testovací REST API službou URL = "https://httpbin.org/put" # hlavička posílaná v dotazu headers = {'accept': 'application/json'} # posláni HTTP dotazu typu PUT response = requests.put(URL, headers=headers) # přečtení hlaviček odpovědi headers = response.headers # výpis všech hlaviček odpovědi print("Headers:") for header_name, header_value in headers.items(): print("{:40s} {}".format(header_name, header_value)) print("-" * 60) print("Content:") # zpracování odpovědi, která přišla ve formátu JSON data = response.json() print(json.dumps(data, indent=4, sort_keys=True))
Po spuštění tohoto skriptu dostaneme následující odpověď (vypsány jsou opět jak hlavičky poslané serverem, tak i tělo odpovědi):
Headers: Connection keep-alive Server gunicorn/19.9.0 Date Wed, 08 Aug 2018 15:25:38 GMT Content-Type application/json Content-Length 371 Access-Control-Allow-Origin * Access-Control-Allow-Credentials true Via 1.1 vegur ------------------------------------------------------------ Content: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "application/json", "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": "https://httpbin.org/put" }
Přístup ke stejnému zdroji, ovšem nepodporovanou metodou GET:
$ curl -v http://httpbin.org/put * Trying 3.220.112.94... * TCP_NODELAY set * Connected to httpbin.org (3.220.112.94) port 80 (#0) > GET /put HTTP/1.1 > Host: httpbin.org > User-Agent: curl/7.55.1 > Accept: */* > < HTTP/1.1 405 METHOD NOT ALLOWED < Date: Wed, 08 Jul 2020 07:16:41 GMT < Content-Type: text/html < Content-Length: 178 < Connection: keep-alive < Server: gunicorn/19.9.0 < Allow: OPTIONS, PUT < 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
Korektní specifikace metody PUT:
$ curl -v -X PUT http://httpbin.org/put * Trying 3.220.112.94... * TCP_NODELAY set * Connected to httpbin.org (3.220.112.94) port 80 (#0) > PUT /put HTTP/1.1 > Host: httpbin.org > User-Agent: curl/7.55.1 > Accept: */* > < HTTP/1.1 200 OK < Date: Wed, 08 Jul 2020 07:17:47 GMT < Content-Type: application/json < Content-Length: 315 < 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-5f05731b-e182f683e9db0d8fc7bd7d15" }, "json": null, "origin": "37.48.9.246", "url": "http://httpbin.org/put" } * Connection #0 to host httpbin.org left intact
9. Požadavek využívající metodu OPTIONS
Poslední HTTP metoda, kterou si v dnešním článku alespoň ve stručnosti popíšeme, se jmenuje OPTIONS. Tato metoda se používá pro zjištění všech operací, které server na dané adrese podporuje (na druhou stranu však nemusí být na straně dotazované služby podporována). Operacemi jsou přitom v tomto kontextu myšleny HTTP metody. V případě, že pošleme serveru HTTP požadavek typu OPTIONS, bude odpověď typicky prázdná (tj. nebude mít žádné tělo), ovšem v hlavičkách odpovědi nalezneme i hlavičku Allow se seznamem podporovaných HTTP metod platných pro danou adresu. Hodnota této hlavičky může vypadat následovně:
Allow OPTIONS, GET, HEAD
Opět se podívejme na demonstrační příklad, který metodu OPTIONS použije, tentokrát na nám již známou adresu https://httpbin.org/get. Namísto volání requests.get() použijeme funkci requests.options():
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests import json # adresa s testovací REST API službou URL = "https://httpbin.org/get" # hlavička posílaná v dotazu headers = {'accept': 'application/json'} # poslání HTTP dotazu typu OPTIONS response = requests.options(URL, headers=headers) # přečtení hlaviček odpovědi headers = response.headers # výpis všech hlaviček odpovědi print("Headers:") for header_name, header_value in headers.items(): print("{:40s} {}".format(header_name, header_value)) print("-" * 60) print("Content:") # výpis těla odpovědi print("Plain text:") print("-" * 60) print(response.text) print("-" * 60)
Povšimněte si, že tělo odpovědi je skutečně prázdné a všechny (meta)informace tedy musíme získat z hlaviček:
Headers: Connection keep-alive Server gunicorn/19.9.0 Date Wed, 08 Aug 2018 19:31:01 GMT Content-Type text/html; charset=utf-8 Allow OPTIONS, GET, HEAD Access-Control-Allow-Origin * Access-Control-Allow-Credentials true Access-Control-Allow-Methods GET, POST, PUT, DELETE, PATCH, OPTIONS Access-Control-Max-Age 3600 Content-Length 0 Via 1.1 vegur ------------------------------------------------------------ Content: Plain text: ------------------------------------------------------------ ------------------------------------------------------------
Stejná operace, ovšem používající nástroj curl:
$ curl -X OPTIONS -v http://httpbin.org/get * Trying 3.220.112.94... * TCP_NODELAY set * Connected to httpbin.org (3.220.112.94) port 80 (#0) > OPTIONS /get HTTP/1.1 > Host: httpbin.org > User-Agent: curl/7.55.1 > Accept: */* > < HTTP/1.1 200 OK < Date: Wed, 08 Jul 2020 08:41:51 GMT < Content-Type: text/html; charset=utf-8 < Content-Length: 0 < Connection: keep-alive < Server: gunicorn/19.9.0 < Allow: HEAD, GET, OPTIONS < Access-Control-Allow-Origin: * < Access-Control-Allow-Credentials: true < Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS < Access-Control-Max-Age: 3600 < * Connection #0 to host httpbin.org left intact
10. Základy práce s cookies
Ve druhé části dnešního článku si ukážeme základy práce s takzvanými cookies. Jedná se o technologii používanou webovými servery i klienty založenými na protokolu HTTP. Samotné cookie je (většinou) malé množství dat, které HTTP server pošle klientovi. Klient si může taková data uložit u sebe, a to buď na delší dobu (cookie jsou ukládány do souboru) nebo po dobu trvání jednoho sezení (session). Cookie mohou sloužit skutečně pouze pro identifikaci sezení (session) a potom je jejich obsah krátký – jen jednoznačný identifikátor session (například JSESSIONID u serverových aplikací naprogramovaných v Javě atd.) nebo je možné do cookie uložit i větší množství dat; například obsah nákupního košíku apod. U cookies je možné specifikovat jak dobu jejich platnosti, tak i adresu/adresy, pro něž je cookie platná (cookie je totiž nutné poslat zpět serveru a potřebujeme zamezit takzvanému kradení cookies). Dnes popisovaná knihovna Requests samozřejmě práci s cookies umožňuje a obsahuje i třídu Session, která je využitelná pro udržení kontextu mezi jednotlivými dotazy, které jsou směřovány na HTTP server.
11. Poslání cookies v GET požadavku
Metodu Requests.get() jsme si již vyzkoušeli v předchozím článku, takže víme, že kromě adresy, která je povinná (první vyžadovaný parametr metody), je možné této metodě předat i další nepovinné údaje, z nichž se následně sestaví požadavek. Jedním z těchto nepovinných údajů je i informace o hlavičkách (známe) a taktéž informace o cookies, které klient předává serveru. Tento parametr se jmenuje přímo cookies a předává se v něm buď slovník obsahující dvojice jméno_cookie+hodnota_cookie nebo objekt typu CookieJar. V dalším demonstračním příkladu je ukázáno, jak se reprezentují cookies formou slovníku, což je samozřejmě (alespoň v Pythonu) nejjednodušší řešení:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests import json # adresa s testovací REST API službou URL = "https://httpbin.org/cookies" # hlavička posílaná v dotazu headers = {'accept': 'application/json'} # příprava cookies cookies = {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'} # poslání HTTP dotazu typu GET response = requests.get(URL, headers=headers, cookies=cookies) # přečtení hlaviček headers = response.headers print("-" * 60) # výpis všech hlaviček print("Headers:") for header_name, header_value in headers.items(): print("{:40s} {}".format(header_name, header_value)) print("-" * 60) print("Content:") # zpracování odpovědi, která přišla ve formátu JSON data = response.json() print(json.dumps(data, indent=4, sort_keys=True)) print("-" * 60) print("Cookies:") print(response.cookies.get_dict())
Server odpoví následujícím způsobem – cookies, které získal, nám vrátí v těle odpovědi, což je praktické zejména s ohledem na ladění aplikací:
------------------------------------------------------------ Headers: Connection keep-alive Server gunicorn/19.9.0 Date Wed, 08 Aug 2018 15:25:42 GMT Content-Type application/json Content-Length 90 Access-Control-Allow-Origin * Access-Control-Allow-Credentials true Via 1.1 vegur ------------------------------------------------------------ Content: { "cookies": { "key1": "value1", "key2": "value2", "key3": "value3" } } ------------------------------------------------------------ Cookies: {}
Předání cookie je pochopitelně možné realizovat i nástrojem curl, jen nesmíme zapomenout na to, že se cookie je ve formě klíč-hodnota:
$ curl -v http://httpbin.org/cookies -b "foo=bar" * Trying 54.236.246.173... * TCP_NODELAY set * Connected to httpbin.org (54.236.246.173) port 80 (#0) > GET /cookies HTTP/1.1 > Host: httpbin.org > User-Agent: curl/7.55.1 > Accept: */* > Cookie: foo=bar > < HTTP/1.1 200 OK < Date: Wed, 08 Jul 2020 07:33:27 GMT < Content-Type: application/json < Content-Length: 40 < Connection: keep-alive < Server: gunicorn/19.9.0 < Access-Control-Allow-Origin: * < Access-Control-Allow-Credentials: true < { "cookies": { "foo": "bar" } } * Connection #0 to host httpbin.org left intact
Větší množství cookies:
$ curl -v http://httpbin.org/cookies -b "foo=bar;baz=xyzzy" * Trying 3.220.112.94... * TCP_NODELAY set * Connected to httpbin.org (3.220.112.94) port 80 (#0) > GET /cookies HTTP/1.1 > Host: httpbin.org > User-Agent: curl/7.55.1 > Accept: */* > Cookie: foo=bar;baz=xyzzy > < HTTP/1.1 200 OK < Date: Wed, 08 Jul 2020 07:33:39 GMT < Content-Type: application/json < Content-Length: 61 < Connection: keep-alive < Server: gunicorn/19.9.0 < Access-Control-Allow-Origin: * < Access-Control-Allow-Credentials: true < { "cookies": { "baz": "xyzzy", "foo": "bar" } } * Connection #0 to host httpbin.org left intact
Nástroj curl dokáže cookies vrácené serverem uložit do souboru:
$ curl curl http://httpbin.org/cookies/set/foo/bar -c session.txt
Obsah souboru session.txt vypadá následovně:
$ cat session.txt # Netscape HTTP Cookie File # https://curl.haxx.se/docs/http-cookies.html # This file was generated by libcurl! Edit at your own risk. httpbin.org FALSE / FALSE 0 foo bar
12. Objekt typu Session a jeho využití pro uložení stavu mezi dotazy
Jak jsme si již řekli v předchozích dvou kapitolách [10] [11], používají se cookies kromě dalších věcí i ve chvíli, kdy je zapotřebí si nějakým způsobem zapamatovat stav nějaké sekvence operací resp. stavu. Typickým příkladem je webový obchod, u něhož si samozřejmě musíme pamatovat přihlášeného uživatele, obsah jeho košíku, zda již bylo za zboží zaplaceno atd. (ostatně podobnou technologii používá i Root). Ve chvíli, kdy je klientská část naprogramována s využitím knihovny Requests, je možné celý stav (možná lépe řečeno „sezení“) reprezentovat objektem typu Session. Požadavky na server se pak posílají odlišně – provádějí se nikoli zavoláním request._http_metoda_, ale session._http_metoda_:
session = requests.Session() URL = "..." # hlavička posílaná v dotazu headers = {'accept': 'application/json'} # poslání HTTP dotazu typu GET return session.get(URL, headers=headers) # poslání HTTP dotazu typu POST return session.post(URL, headers=headers)
atd. atd.
Příklad, který objekt typu Session skutečně používá, si ukážeme v navazující kapitole.
13. Příklad na použití objektu typu Session
Další demonstrační příklad je již poněkud komplikovanější, protože v něm využíváme objekt typu Session, který si pamatuje stav požadavků (nebo možná lépe řečeno kontext, v jehož rámci se požadavky posílají a zpracovávají). V tomto příkladu voláme dvojici endpointů testovací HTTP služby https://httpbin.org/ pro nastavení nových cookies, popř. pro jejich odstranění ze sezení (session):
- https://httpbin.org/cookies/set/{name}/{value} pro nastavení cookie se zadaným jménem a hodnotou
- https://httpbin.org/cookies/delete?{name} pro vymazání cookie specifikovaného jména
Zdrojový kód tohoto příkladu vypadá následovně:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests import json def set_cookie(session, name, value): # adresa s testovací REST API službou URL = "https://httpbin.org/cookies/set/{name}/{value}".format(name=name, value=value) # hlavička posílaná v dotazu headers = {'accept': 'application/json'} # poslani HTTP dotazu typu GET return session.get(URL, headers=headers) def delete_cookie(session, name): # adresa s testovací REST API službou URL = "https://httpbin.org/cookies/delete?{name}=".format(name=name) # hlavička posílaná v dotazu headers = {'accept': 'application/json'} # poslani HTTP dotazu typu GET return session.get(URL, headers=headers) def print_response(response): # přečtení hlaviček headers = response.headers print("-" * 60) # výpis hlavicek print("Headers:") for header_name, header_value in headers.items(): print("{:40s} {}".format(header_name, header_value)) print("-" * 60) print("Content:") # zpracovani odpovedi, ktera prisla ve formatu JSON data = response.text # zpracovani odpovedi, ktera prisla ve formatu JSON data = response.json() print(json.dumps(data, indent=4, sort_keys=True)) print("-" * 60) def print_session_cookies(session): cookies = session.cookies print("Session cookies:") for cookie_name, cookie_value in cookies.items(): print("{:40s} {}".format(cookie_name, cookie_value)) print("-" * 60) session = requests.Session() print("*** set cookie 'foo'=6 ***") response = set_cookie(session, "foo", "6") print_response(response) print_session_cookies(session) print() print("*** set cookie 'bar'=7 ***") response = set_cookie(session, "bar", "7") print_response(response) print_session_cookies(session) print() print("*** set cookie 'foo'=42 ***") response = set_cookie(session, "foo", "42") print_response(response) print_session_cookies(session) print() print("*** delete cookie 'foo' ***") response = delete_cookie(session, "foo") print_response(response) print_session_cookies(session) print() print("*** delete cookie 'baz' ***") response = delete_cookie(session, "baz") print_response(response) print_session_cookies(session) print()
Podívejme se nyní na výsledky. Jsou poněkud delší, protože ukazují, jak se dá zcela jednoduše měnit stav sezení:
*** set cookie 'foo'=6 *** ------------------------------------------------------------ Headers: Connection keep-alive Server gunicorn/19.9.0 Date Wed, 08 Aug 2018 15:25:57 GMT Content-Type application/json Content-Length 38 Access-Control-Allow-Origin * Access-Control-Allow-Credentials true Via 1.1 vegur ------------------------------------------------------------ Content: { "cookies": { "foo": "6" } } ------------------------------------------------------------ Session cookies: foo 6 ------------------------------------------------------------ *** set cookie 'bar'=7 *** ------------------------------------------------------------ Headers: Connection keep-alive Server gunicorn/19.9.0 Date Wed, 08 Aug 2018 15:25:57 GMT Content-Type application/json Content-Length 55 Access-Control-Allow-Origin * Access-Control-Allow-Credentials true Via 1.1 vegur ------------------------------------------------------------ Content: { "cookies": { "bar": "7", "foo": "6" } } ------------------------------------------------------------ Session cookies: bar 7 foo 6 ------------------------------------------------------------ *** set cookie 'foo'=42 *** ------------------------------------------------------------ Headers: Connection keep-alive Server gunicorn/19.9.0 Date Wed, 08 Aug 2018 15:25:57 GMT Content-Type application/json Content-Length 56 Access-Control-Allow-Origin * Access-Control-Allow-Credentials true Via 1.1 vegur ------------------------------------------------------------ Content: { "cookies": { "bar": "7", "foo": "42" } } ------------------------------------------------------------ Session cookies: bar 7 foo 42 ------------------------------------------------------------ *** delete cookie 'foo' *** ------------------------------------------------------------ Headers: Connection keep-alive Server gunicorn/19.9.0 Date Wed, 08 Aug 2018 15:25:57 GMT Content-Type application/json Content-Length 38 Access-Control-Allow-Origin * Access-Control-Allow-Credentials true Via 1.1 vegur ------------------------------------------------------------ Content: { "cookies": { "bar": "7" } } ------------------------------------------------------------ Session cookies: bar 7 ------------------------------------------------------------ *** delete cookie 'baz' *** ------------------------------------------------------------ Headers: Connection keep-alive Server gunicorn/19.9.0 Date Wed, 08 Aug 2018 15:25:58 GMT Content-Type application/json Content-Length 38 Access-Control-Allow-Origin * Access-Control-Allow-Credentials true Via 1.1 vegur ------------------------------------------------------------ Content: { "cookies": { "bar": "7" } } ------------------------------------------------------------ Session cookies: bar 7 ------------------------------------------------------------
14. Úprava testovacího HTTP serveru takovým způsobem, aby vypisoval hlavičky posílané klientem
Na závěr si ukážeme, v jakém formátu se vlastně informace o cookies přenáší na server a jak je server dokáže zpracovat. Již dopředu si řekněme, že je vše řízeno dvěma hlavičkami se jmény Cookie a Set-Cookie. Abychom si ukázali způsob předávání těchto hlaviček, upravíme náš původní lokální (testovací) HTTP server, jehož první variantu naleznete na adrese https://github.com/tisnik/testing-in-python/tree/master/requests/simple_server.py a upravenou variantu na adrese https://github.com/tisnik/testing-in-python/tree/master/requests/simple_server2.py. Úprava bude spočívat v tom, že server bude vypisovat všechny hlavičky požadavků, a to nezávisle na tom, jaká HTTP metoda byla pro poslání požadavku použita.
15. Nová podoba testovacího HTTP serveru
Podoba zdrojového kódu nové varianty testovacího HTTP serveru je vypsána pod tímto odstavcem. Oproti původní variantě byly přidány pomocné metody send_headers() a print_request_content(). Server bude otestován skriptem popsaným v navazující kapitole:
#!/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 print_uri(self): print("URI: {uri}".format(uri=self.path)) def send_headers(self): # příprava hlavicky odpovedi self.send_response(200) self.send_header('Content-type', 'text/plain') self.end_headers() def print_request_content(self): # přečtení těla HTTP požadavku print(self.headers) if "Content-Length" in self.headers: 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)) def do_GET(self): self.print_uri() self.print_request_content() # odpověď serveru klientovi self.send_headers() self.wfile.write("*** get ***".encode("utf-8")) def do_POST(self): self.print_uri() self.print_request_content() # odpověď serveru klientovi self.send_headers() self.wfile.write("*** post ***".encode("utf-8")) simpleServer = HTTPServer((hostName, hostPort), SimpleServer) try: simpleServer.serve_forever() except KeyboardInterrupt: pass simpleServer.server_close()
16. Skript pro volání testovacího HTTP serveru s hlavičkou Set-Cookie
Podívejme se nyní na jednoduchý skript, který zavolá náš testovací HTTP server. Použije přitom metodu GET (koncovka adresy je libovolná, protože je serverem zcela ignorována) a předá serveru tři cookies:
# příprava cookies cookies = {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'} # poslání HTTP dotazu typu GET response = requests.get(URL, headers=headers, cookies=cookies)
Navíc se ještě předá přímo v hlavičkách požadavku hlavička pojmenovaná Set-Cookie se jménem a hodnotou cookie, která by se měla zapamatovat (a většinou poslat zpět klientovi):
# hlavička posílaná v dotazu headers = {'accept': 'application/json', 'Set-Cookie': "x=y"} # poslání HTTP dotazu typu GET response = requests.get(URL, headers=headers, cookies=cookies)
Úplný zdrojový kód tohoto skriptu vypadá následovně:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import requests # adresa s testovací REST API službou URL = "http://localhost:8000" # hlavička posílaná v dotazu headers = {'accept': 'application/json', 'Set-Cookie': "x=y"} # příprava cookies cookies = {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'} # poslání HTTP dotazu typu GET response = requests.get(URL, headers=headers, cookies=cookies) # přečtení hlaviček headers = response.headers print("-" * 60) # výpis všech hlaviček print("Headers:") for header_name, header_value in headers.items(): print("{:40s} {}".format(header_name, header_value)) print("-" * 60) print("Content:") # zpracování odpovědi, která přišla ve formátu JSON data = response.text print(data) print("-" * 60) print("Cookies:") print(response.cookies.get_dict())
17. Spuštění skriptu, ukázka celého tvaru požadavku a formát odpovědi serveru
Před otestováním skriptu popsaného v předchozí kapitole je nutné spustit náš testovací HTTP server v samostatném terminálu (aby bylo vidět jeho výstup). To se provede jednoduše příkazem (skript se serverem je spustitelný):
$ ./simple_server_2.py
Nyní již můžeme (opět v jiném terminálu) spustit zmíněný skript:
$ ./21_cookies_test_against_local_server.py
Tento skript by měl poslat požadavek na testovací HTTP server, získat od něj odpověď a následně vypsat přibližně následující údaje (může se samozřejmě lišit čas, konkrétní verze interpretru Pythonu atd.). Povšimněte si, že server podle všech předpokladů nevrátil žádné cookies:
------------------------------------------------------------ Headers: Server BaseHTTP/0.6 Python/3.6.3 Date Thu, 09 Aug 2018 12:11:39 GMT Content-type text/plain ------------------------------------------------------------ Content: *** get *** ------------------------------------------------------------ Cookies: {}
Zajímavější bude pohled na zprávu vypsanou serverem ve chvíli, kdy přijme dotaz od demonstračního skriptu. Povšimněte si především dvou hlaviček nazvaných Cookie a Set-Cookie. V hlavičce Cookie jsou v textové podobě jména a hodnoty všech předaných cookies, v hlavičce Set-Cookie pak přesný opis textu, který jsme předali v požadavku (což je logické, ovšem je patrné, že zde máme velkou volnost zápisu, tj. serveru lze například předat data, která nebude schopen zpracovat):
Cookie: key1=value1; key2=value2; key3=value3 Set-Cookie: x=y
URI: / Host: localhost:8000 User-Agent: python-requests/2.13.0 Accept-Encoding: gzip, deflate accept: application/json Connection: keep-alive Set-Cookie: x=y Cookie: key1=value1; key2=value2; key3=value3 127.0.0.1 - - [09/Aug/2018 14:11:39] "GET / HTTP/1.1" 200 -
Nic nám pochopitelně nebrání komunikovat s lokálně běžícím HTTP serverem z nástroje curl, z webového browseru (což však nemá praktický význam) či z telnetu.
Použití nástroje curl vůči lokálně běžícímu HTTP serveru:
$ curl -v localhost:8000 -b "foo=bar;baz=xyzzy" * 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: */* > Cookie: foo=bar;baz=xyzzy > * HTTP 1.0, assume close after body < HTTP/1.0 200 OK < Server: BaseHTTP/0.6 Python/3.6.6 < Date: Wed, 08 Jul 2020 09:50:19 GMT < Content-type: text/plain < * Closing connection 0
HTTP server tento požadavek zpracuje následujícím způsobem:
URI: / Host: localhost:8000 User-Agent: curl/7.55.1 Accept: */* 127.0.0.1 - - [08/Jul/2020 11:49:57] "GET / HTTP/1.1" 200 - URI: / Host: localhost:8000 User-Agent: curl/7.55.1 Accept: */* Cookie: foo=bar;baz=xyzzy 127.0.0.1 - - [08/Jul/2020 11:50:19] "GET / HTTP/1.1" 200 -
Použití nástroje telnet poměrně dobře ukazuje, jakým způsobem je strukturován HTTP požadavek (request):
$ telnet localhost 8000 Trying ::1... telnet: connect to address ::1: Connection refused Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. GET / Cookie: foo=bar *** 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:
- 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/ - Testování webových aplikací s REST API z Pythonu
https://www.root.cz/clanky/testovani-webovych-aplikaci-s-rest-api-z-pythonu/ - 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
- 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/ - 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/