Hlavní navigace

Testování webových aplikací s REST API z Pythonu

 Autor: Depositphotos
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 spouštěny samostatně (vůči jedné izolované komponentě), nebo mohou být součástí end-to-end testů.
Pavel Tišnovský 7. 7. 2020
Doba čtení: 45 minut

Sdílet

11. Zpracování odpovědi, která byla vrácena ve formátu JSON

12. Použití HTTP metody POST

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

16. Implementace HTTP serveru

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

20. Odkazy na Internetu

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ů:

  1. Jednotkové testy
  2. Testy komponent a integrační testy
  3. Systémové testy, akceptační testy
  4. Testy aplikačního (programového) rozhraní
  5. Testy grafického uživatelského rozhraní a end-to-end testy
  6. 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 Requestscurl 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"
}
Poznámka: znakem > jsou uvozeny data požadavku (request), znakem < pak hlavička odpovědi serveru (response). Samotný text odpovědi není nijak uvozen a obsahuje data ve formátu JSON.

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.
Poznámka: můžeme vidět, že server poslal odpověď a následně se odpojil (poslední řádek není součástí odpovědi, ale hlášení telnetu).

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]>
Poznámka: povšimněte si, že je možné bez problémů použít i protokol HTTPS namísto pouhého HTTP. Od této skutečnosti je programátor využívající knihovnu Requests většinou odstíněn. Varianta s HTTPS:
#!/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

Totéž v Pythonu:

#!/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)
Poznámka: ve skutečnosti je možné adresu http://httpbin.org/status/XYZ zavolat s libovolným stavovým kódem, který má být vrácený v odpovědi, a to včetně kódů neznámých:
$ 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&parametr2=hodnota2&parametr2=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"
}

------------------------------------------------------------
Poznámka: povšimněte si, že testovací server nám vlastně pouze vrací údaje, které od nás získal, což je dobře, protože ho skutečně budeme moci použít pro další pokusy.

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"
}
 
------------------------------------------------------------
Poznámka: hlavička content_length nemusí být (a neměla by být) nastavena ve chvíli, kdy je tělo odpovědi prázdné, viz například https://tools.ietf.org/html/rfc7230. Některé servery či služby navíc v této hlavičce neposílají správné údaje – někdy to vlastně ani není možné, protože odpověď serveru je tvořena „on-the-fly“, tj. průběžně.

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)
Poznámka: předchozí chyba vznikla při zpracování odpovědi z adresy http://httpbin.org/bytes/100.

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"
}
Poznámka: hodnota atributu question je nyní vrácena a je rovna null, což je opět potenciálně velmi důležité.

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/brad­montgomery/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):

Hackinbg

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 -
Poznámka: nic nám pochopitelně nebrání komunikovat s naším serverem s využitím nástrojů curl, telnet atd.:
$ 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.
Poznámka: při použití telnetu je nutné za příkaz GET vložit jeden prázdný řádek (dvojí stlačení klávesy Enter).

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:

# Příklad Stručný popis Cesta
1 01_basic_usage.py použití metody GET pro komunikaci s HTTP serverem https://github.com/tisnik/testing-in-python/tree/master/reques­ts/01_basic_usage.py
2 02_check_status.py kontrola stavového kódu odpovědi HTTP serveru https://github.com/tisnik/testing-in-python/tree/master/reques­ts/02_check_status.py
3 02_any_status.py kontrola stavového kódu odpovědi HTTP serveru https://github.com/tisnik/testing-in-python/tree/master/reques­ts/02_any_status.py
4 03_content_type.py získání typu/formátu odpovědi serveru https://github.com/tisnik/testing-in-python/tree/master/reques­ts/03_content_type.py
5 03_other_content_types.py získání typu/formátu odpovědi serveru https://github.com/tisnik/testing-in-python/tree/master/reques­ts/03_other_content_types­.py
6 04_response_content.py získání obsahu odpovědi serveru https://github.com/tisnik/testing-in-python/tree/master/reques­ts/04_response_content.py
7 05_response_content_and_headers.py získání obsahu odpovědi serveru včetně hlaviček HTTP protokolu https://github.com/tisnik/testing-in-python/tree/master/reques­ts/05_response_content_an­d_headers.py
8 06_response_json.py práce s odpovědí ve formátu JSON https://github.com/tisnik/testing-in-python/tree/master/reques­ts/06_response_json.py
9 07_post_method.py použití metody POST pro komunikaci se serverem https://github.com/tisnik/testing-in-python/tree/master/reques­ts/07_post_method.py
10 08_post_method_with_payload.py metoda POST a data poslaná ve formuláři https://github.com/tisnik/testing-in-python/tree/master/reques­ts/08_post_method_with_pa­yload.py
11 09_post_method_with_payload.py metoda POST a data poslaná v těle zprávy https://github.com/tisnik/testing-in-python/tree/master/reques­ts/09_post_method_with_pa­yload.py
12 10_against_test_local_server.py test metod GET a POST vůči lokálnímu serveru https://github.com/tisnik/testing-in-python/tree/master/reques­ts/10_against_test_local_ser­ver.py
       
13 simple_server.py velmi jednoduchý lokální HTTP server https://github.com/tisnik/testing-in-python/tree/master/reques­ts/simple_server.py
14 simple_server2.py druhá varianta HTTP serveru https://github.com/tisnik/testing-in-python/tree/master/reques­ts/simple_server2.py

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:

  1. Requests: HTTP for Humans (dokumentace)
    http://docs.python-requests.org/en/master/
  2. Requests: Introduction
    http://docs.python-requests.org/en/latest/user/intro/
  3. Requests na GitHubu
    https://github.com/requests/requests
  4. Requests (software) na Wikipedii
    https://en.wikipedia.org/wi­ki/Requests_%28software%29
  5. Pip (dokumentace)
    https://pip.pypa.io/en/stable/
  6. 20 Python libraries you can’t live without
    https://pythontips.com/2013/07/30/20-python-libraries-you-cant-live-without/
  7. 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
  8. Python: useful modules
    https://wiki.python.org/mo­in/UsefulModules
  9. Top 15 most popular Python libraries
    https://keyua.org/blog/most-popular-python-libraries/
  10. Hypertext Transfer Protocol
    https://en.wikipedia.org/wi­ki/Hypertext_Transfer_Pro­tocol
  11. List of HTTP header fields
    https://en.wikipedia.org/wi­ki/List_of_HTTP_header_fi­elds
  12. List of HTTP status codes
    https://en.wikipedia.org/wi­ki/List_of_HTTP_status_co­des
  13. Python requests deep dive
    https://medium.com/@antho­nypjshaw/python-requests-deep-dive-a0a5c5c1e093
  14. The awesome requests module
    https://www.pythonforbegin­ners.com/requests/the-awesome-requests-module
  15. Send HTTP Requests in Python
    https://code-maven.com/http-requests-in-python
  16. Introducing JSON
    http://json.org/
  17. 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/
  18. 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/
  19. 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/
  20. 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/
  21. 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/
  22. Struktura projektů s jednotkovými testy, využití Travis CI
    https://www.root.cz/clanky/struktura-projektu-s-jednotkovymi-testy-vyuziti-travis-ci/
  23. Omezení stavového prostoru testovaných funkcí a metod
    https://www.root.cz/clanky/omezeni-stavoveho-prostoru-testovanych-funkci-a-metod/
  24. Testování aplikací s využitím nástroje Hypothesis
    https://www.root.cz/clanky/testovani-aplikaci-s-vyuzitim-nastroje-hypothesis/
  25. Testování aplikací s využitím nástroje Hypothesis (dokončení)
    https://www.root.cz/clanky/testovani-aplikaci-s-vyuzitim-nastroje-hypothesis-dokonceni/
  26. 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/
  27. 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/
  28. 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/
  29. 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/
  30. Validace datových struktur v Pythonu (2. část)
    https://www.root.cz/clanky/validace-datovych-struktur-v-pythonu-2-cast/
  31. Validace datových struktur v Pythonu (dokončení)
    https://www.root.cz/clanky/validace-datovych-struktur-v-pythonu-dokonceni/
  32. Univerzální testovací nástroj Robot Framework
    https://www.root.cz/clanky/univerzalni-testovaci-nastroj-robot-framework/
  33. Univerzální testovací nástroj Robot Framework a BDD testy
    https://www.root.cz/clanky/univerzalni-testovaci-nastroj-robot-framework-a-bdd-testy/
  34. Úvod do problematiky fuzzingu a fuzz testování
    https://www.root.cz/clanky/uvod-do-problematiky-fuzzingu-a-fuzz-testovani/
  35. Ú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/
  36. 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/
  37. 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/
  38. Testování aplikací naprogramovaných v jazyce Go
    https://www.root.cz/clanky/testovani-aplikaci-naprogramovanych-v-jazyce-go/
  39. 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/
  40. 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/
  41. 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/
  42. 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/
  43. 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/
  44. 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/
  45. 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/
  46. 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

  1. Writing tests for RESTful APIs in Python using requests – part 1: basic tests
    https://www.ontestautomati­on.com/writing-tests-for-restful-apis-in-python-using-requests-part-1-basic-tests/
  2. Step by Step Rest API Testing using Python + Pytest + Allure
    https://www.udemy.com/course/api-testing-python/
  3. Prime formulas and polynomial functions
    https://en.wikipedia.org/wi­ki/Formula_for_primes#Pri­me_formulas_and_polynomial_fun­ctions
  4. Prime-Generating Polynomial
    https://mathworld.wolfram.com/Prime-GeneratingPolynomial.html
  5. Hoare logic
    https://en.wikipedia.org/wi­ki/Hoare_logic
  6. Goto Fail, Heartbleed, and Unit Testing Culture
    https://martinfowler.com/ar­ticles/testing-culture.html
  7. PEP-484
    https://www.python.org/dev/peps/pep-0484/
  8. In-depth: Functional programming in C++
    https://www.gamasutra.com/vi­ew/news/169296/Indepth_Fun­ctional_programming_in_C.php
  9. mypy
    http://www.mypy-lang.org/
  10. Welcome to Mypy documentation!
    https://mypy.readthedocs.i­o/en/latest/index.html
  11. mypy na GitHubu
    https://github.com/python/mypy
  12. mypy 0.770 na PyPi
    https://pypi.org/project/mypy/
  13. Extensions for mypy (separated out from mypy/extensions)
    https://github.com/python/my­py_extensions
  14. The Mypy Blog
    https://mypy-lang.blogspot.com/2020/03/mypy-0770-released.html
  15. Our journey to type checking 4 million lines of Python
    https://dropbox.tech/application/our-journey-to-type-checking-4-million-lines-of-python
  16. Type-Checking Python Programs With Type Hints and mypy
    https://www.youtube.com/wat­ch?v=2×WhaALHTvU
  17. Refactoring to Immutability – Kevlin Henney
    https://www.youtube.com/wat­ch?v=APUCMSPiNh4
  18. Bernat Gabor – Type hinting (and mypy) – PyCon 2019
    https://www.youtube.com/wat­ch?v=hTrjTAPnA_k
  19. Stanford Seminar – Optional Static Typing for Python
    https://www.youtube.com/wat­ch?v=GiZKuyLKvAA
  20. mypy Getting to Four Million Lines of Typed Python – Michael Sullivan
    https://www.youtube.com/wat­ch?v=FT_WHV4-QcU
  21. Shebang
    https://en.wikipedia.org/wi­ki/Shebang_(Unix)
  22. pytest 5.4.2 na PyPi
    https://pypi.org/project/pytest/
  23. Hillel Wayne – Beyond Unit Tests: Taking Your Testing to the Next Level – PyCon 2018
    https://www.youtube.com/wat­ch?v=MYucYon2-lk
  24. Awesome Python – testing
    https://github.com/vinta/awesome-python#testing
  25. pytest Plugins Compatibility
    http://plugincompat.herokuapp.com/
  26. Selenium (pro Python)
    https://pypi.org/project/selenium/
  27. Getting Started With Testing in Python
    https://realpython.com/python-testing/
  28. unittest.mock — mock object library
    https://docs.python.org/3­.5/library/unittest.mock.html
  29. mock 2.0.0
    https://pypi.python.org/pypi/mock
  30. An Introduction to Mocking in Python
    https://www.toptal.com/python/an-introduction-to-mocking-in-python
  31. Mock – Mocking and Testing Library
    http://mock.readthedocs.io/en/stable/
  32. Python Mocking 101: Fake It Before You Make It
    https://blog.fugue.co/2016–02–11-python-mocking-101.html
  33. Nauč se Python! – Testování
    http://naucse.python.cz/les­sons/intro/testing/
  34. Flexmock (dokumentace)
    https://flexmock.readthedoc­s.io/en/latest/
  35. Test Fixture (Wikipedia)
    https://en.wikipedia.org/wi­ki/Test_fixture
  36. Mock object (Wikipedia)
    https://en.wikipedia.org/wi­ki/Mock_object
  37. Extrémní programování
    https://cs.wikipedia.org/wi­ki/Extr%C3%A9mn%C3%AD_pro­gramov%C3%A1n%C3%AD
  38. Programování řízené testy
    https://cs.wikipedia.org/wi­ki/Programov%C3%A1n%C3%AD_%C5%99%­C3%ADzen%C3%A9_testy
  39. Pip (dokumentace)
    https://pip.pypa.io/en/stable/
  40. Tox
    https://tox.readthedocs.io/en/latest/
  41. pytest: helps you write better programs
    https://docs.pytest.org/en/latest/
  42. doctest — Test interactive Python examples
    https://docs.python.org/dev/li­brary/doctest.html#module-doctest
  43. unittest — Unit testing framework
    https://docs.python.org/dev/li­brary/unittest.html
  44. Python namespaces
    https://bytebaker.com/2008/07/30/pyt­hon-namespaces/
  45. Namespaces and Scopes
    https://www.python-course.eu/namespaces.php
  46. Stránka projektu Robot Framework
    https://robotframework.org/
  47. GitHub repositář Robot Frameworku
    https://github.com/robotfra­mework/robotframework
  48. Robot Framework (Wikipedia)
    https://en.wikipedia.org/wi­ki/Robot_Framework
  49. Tutoriál Robot Frameworku
    http://www.robotframeworktu­torial.com/
  50. Robot Framework Documentation
    https://robotframework.or­g/robotframework/
  51. Robot Framework Introduction
    https://blog.testproject.i­o/2016/11/22/robot-framework-introduction/
  52. robotframework 3.1.2 na PyPi
    https://pypi.org/project/ro­botframework/
  53. Robot Framework demo (GitHub)
    https://github.com/robotfra­mework/RobotDemo
  54. Robot Framework web testing demo using SeleniumLibrary
    https://github.com/robotfra­mework/WebDemo
  55. Robot Framework for Mobile Test Automation Demo
    https://www.youtube.com/wat­ch?v=06LsU08slP8
  56. Gherkin
    https://cucumber.io/docs/gherkin/
  57. Selenium
    https://selenium.dev/
  58. SeleniumLibrary
    https://robotframework.org/
  59. The Practical Test Pyramid
    https://martinfowler.com/ar­ticles/practical-test-pyramid.html
  60. Acceptance Tests and the Testing Pyramid
    http://www.blog.acceptance­testdrivendevelopment.com/ac­ceptance-tests-and-the-testing-pyramid/
  61. Tab-separated values
    https://en.wikipedia.org/wiki/Tab-separated_values
  62. A quick guide about Python implementations
    https://blog.rmotr.com/a-quick-guide-about-python-implementations-aa224109f321
  63. radamsa
    https://gitlab.com/akihe/radamsa
  64. Fuzzing (Wikipedia)
    https://en.wikipedia.org/wiki/Fuzzing
  65. american fuzzy lop
    http://lcamtuf.coredump.cx/afl/
  66. Fuzzing: the new unit testing
    https://go-talks.appspot.com/github.com/dvyukov/go-fuzz/slides/fuzzing.slide#1
  67. Corpus for github.com/dvyukov/go-fuzz examples
    https://github.com/dvyukov/go-fuzz-corpus
  68. AFL – QuickStartGuide.txt
    https://github.com/google/AF­L/blob/master/docs/QuickStar­tGuide.txt
  69. Introduction to Fuzzing in Python with AFL
    https://alexgaynor.net/2015/a­pr/13/introduction-to-fuzzing-in-python-with-afl/
  70. Writing a Simple Fuzzer in Python
    https://jmcph4.github.io/2018/01/19/wri­ting-a-simple-fuzzer-in-python/
  71. How to Fuzz Go Code with go-fuzz (Continuously)
    https://fuzzit.dev/2019/10/02/how-to-fuzz-go-code-with-go-fuzz-continuously/
  72. Golang Fuzzing: A go-fuzz Tutorial and Example
    http://networkbit.ch/golang-fuzzing/
  73. Fuzzing Python Modules
    https://stackoverflow.com/qu­estions/20749026/fuzzing-python-modules
  74. 0×3 Python Tutorial: Fuzzer
    http://www.primalsecurity.net/0×3-python-tutorial-fuzzer/
  75. fuzzing na PyPi
    https://pypi.org/project/fuzzing/
  76. Fuzzing 0.3.2 documentation
    https://fuzzing.readthedoc­s.io/en/latest/
  77. Randomized testing for Go
    https://github.com/dvyukov/go-fuzz
  78. HTTP/2 fuzzer written in Golang
    https://github.com/c0nrad/http2fuzz
  79. 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
  80. Continuous Fuzzing Made Simple
    https://fuzzit.dev/
  81. Halt and Catch Fire
    https://en.wikipedia.org/wi­ki/Halt_and_Catch_Fire#In­tel_x86
  82. Random testing
    https://en.wikipedia.org/wi­ki/Random_testing
  83. Monkey testing
    https://en.wikipedia.org/wi­ki/Monkey_testing
  84. Fuzzing for Software Security Testing and Quality Assurance, Second Edition
    https://books.google.at/bo­oks?id=tKN5DwAAQBAJ&pg=PR15&lpg=PR15&q=%­22I+settled+on+the+term+fuz­z%22&redir_esc=y&hl=de#v=o­nepage&q=%22I%20settled%20on%20the%20ter­m%20fuzz%22&f=false
  85. libFuzzer – a library for coverage-guided fuzz testing
    https://llvm.org/docs/LibFuzzer.html
  86. fuzzy-swagger na PyPi
    https://pypi.org/project/fuzzy-swagger/
  87. fuzzy-swagger na GitHubu
    https://github.com/namuan/fuzzy-swagger
  88. Fuzz testing tools for Python
    https://wiki.python.org/mo­in/PythonTestingToolsTaxo­nomy#Fuzz_Testing_Tools
  89. A curated list of awesome Go frameworks, libraries and software
    https://github.com/avelino/awesome-go
  90. gofuzz: a library for populating go objects with random values
    https://github.com/google/gofuzz
  91. tavor: A generic fuzzing and delta-debugging framework
    https://github.com/zimmski/tavor
  92. hypothesis na GitHubu
    https://github.com/Hypothe­sisWorks/hypothesis
  93. Hypothesis: Test faster, fix more
    https://hypothesis.works/
  94. Hypothesis
    https://hypothesis.works/ar­ticles/intro/
  95. What is Hypothesis?
    https://hypothesis.works/articles/what-is-hypothesis/
  96. What is Property Based Testing?
    https://hypothesis.works/articles/what-is-property-based-testing/
  97. Databáze CVE
    https://www.cvedetails.com/
  98. Fuzz test Python modules with libFuzzer
    https://github.com/eerimoq/pyfuzzer
  99. Taof – The art of fuzzing
    https://sourceforge.net/pro­jects/taof/
  100. JQF + Zest: Coverage-guided semantic fuzzing for Java
    https://github.com/rohanpadhye/jqf
  101. http2fuzz
    https://github.com/c0nrad/http2fuzz
  102. Demystifying hypothesis testing with simple Python examples
    https://towardsdatascience­.com/demystifying-hypothesis-testing-with-simple-python-examples-4997ad3c5294
  103. Testování
    http://voho.eu/wiki/testovani/
  104. Unit testing (Wikipedia.en)
    https://en.wikipedia.org/wi­ki/Unit_testing
  105. Unit testing (Wikipedia.cz)
    https://cs.wikipedia.org/wi­ki/Unit_testing
  106. Unit Test vs Integration Test
    https://www.youtube.com/wat­ch?v=0GypdsJulKE
  107. TestDouble
    https://martinfowler.com/bli­ki/TestDouble.html
  108. Test Double
    http://xunitpatterns.com/Tes­t%20Double.html
  109. Test-driven development (Wikipedia)
    https://en.wikipedia.org/wiki/Test-driven_development
  110. Acceptance test–driven development
    https://en.wikipedia.org/wi­ki/Acceptance_test%E2%80%93dri­ven_development
  111. Gauge
    https://gauge.org/
  112. Gauge (software)
    https://en.wikipedia.org/wi­ki/Gauge_(software)
  113. PYPL PopularitY of Programming Language
    https://pypl.github.io/PYPL.html
  114. Testing is Good. Pyramids are Bad. Ice Cream Cones are the Worst
    https://medium.com/@fistsOf­Reason/testing-is-good-pyramids-are-bad-ice-cream-cones-are-the-worst-ad94b9b2f05f
  115. Články a zprávičky věnující se Pythonu
    https://www.root.cz/n/python/
  116. PythonTestingToolsTaxonomy
    https://wiki.python.org/mo­in/PythonTestingToolsTaxo­nomy
  117. Top 6 BEST Python Testing Frameworks [Updated 2020 List]
    https://www.softwaretestin­ghelp.com/python-testing-frameworks/
  118. pytest-print 0.1.3
    https://pypi.org/project/pytest-print/
  119. pytest fixtures: explicit, modular, scalable
    https://docs.pytest.org/en/la­test/fixture.html
  120. PyTest Tutorial: What is, Install, Fixture, Assertions
    https://www.guru99.com/pytest-tutorial.html
  121. Pytest – Fixtures
    https://www.tutorialspoin­t.com/pytest/pytest_fixtu­res.htm
  122. Marking test functions with attributes
    https://docs.pytest.org/en/la­test/mark.html
  123. pytest-print
    https://pytest-print.readthedocs.io/en/latest/
  124. Continuous integration
    https://en.wikipedia.org/wi­ki/Continuous_integration
  125. Travis CI
    https://travis-ci.org/
  126. Mutation testing
    https://en.wikipedia.org/wi­ki/Mutation_testing
  127. Články o Hypothesis
    https://news.ycombinator.com/from?si­te=hypothesis.works
  128. Testovací případ
    https://cs.wikipedia.org/wi­ki/Testovac%C3%AD_p%C5%99%C3%AD­pad
  129. Most testing is ineffective
    https://hypothesis.works/