Základy Pytestu

Dokončeno

Pojďme začít testovat pomocí Pytestu. Jak jsme zmínili v předchozí lekci, Pytest je vysoce konfigurovatelný a dokáže zpracovat komplexní testovací sady. Ale k zahájení psaní testů to nevyžaduje moc. Ve skutečnosti jednodušší architektura umožňuje psát testy, tím lépe.

Na konci této části byste měli mít vše, co potřebujete, abyste mohli začít psát první testy a spouštět je pomocí Pytestu.

Konvence

Než se ponoříme do psaní testů, musíme pokrýt některé z testovacích konvencí, na které Pytest spoléhá.

Testovací soubory, testovací adresáře ani obecná rozložení testování v Pythonu nejsou žádná pevná pravidla. Když znáte tato pravidla, můžete využít automatické zjišťování a spouštění testů bez nutnosti jakékoli další konfigurace.

Testování adresářů a testovacích souborů

Hlavním adresářem pro testy je adresář testů . Tento adresář můžete umístit na kořenovou úroveň projektu, ale není neobvyklé ho vidět společně s moduly kódu.

Poznámka:

V tomto modulu použijeme ve výchozím nastavení testy v kořenovém adresáři projektu.

Pojďme se podívat, jak vypadá kořen malého projektu Pythonu s názvem jformat :

.
├── README.md
├── jformat
│   ├── __init__.py
│   └── main.py
├── setup.py
└── tests
    └── test_main.py

Adresář testů je v kořenovém adresáři projektu s jedním testovacím souborem. V tomto případě se testovací soubor nazývá test_main.py. Tento příklad ukazuje dvě kritické konvence:

  • K umístění testovacích souborů a vnořených testovacích adresářů použijte adresář testů.
  • Předpona testovacích souborů s testem Předpona označuje, že soubor obsahuje testovací kód.

Upozornění

test Nepoužívejte jako název adresáře (jednotný formulář). Název test je modul Pythonu, takže vytvoření adresáře s názvem stejné by ho přepsalo. Místo toho vždy používejte množné číslo tests .

Funkce pro testování

Jedním ze silných argumentů pro použití Pytestu je, že umožňuje psát testovací funkce. Podobně jako u testovacích souborů musí být testovací funkce předponou test_. Předpona test_ zajistí, že Pytest shromáždí test a spustí ho.

Jednoduchá testovací funkce vypadá takto:

def test_main():
    assert "a string value" == "a string value"

Poznámka:

Pokud znáte unittest, může být překvapením, že se při použití testovací funkce používá assert . Prosté tvrzení probereme podrobněji později, ale s Pytestem získáte bohaté hlášení o chybách pomocí prostých kontrolních výrazů.

Testovací třídy a testovací metody

Podobně jako konvence pro soubory a funkce používají testovací třídy a metody testování následující konvence:

  • Třídy testů mají předponu Test
  • Testovací metody mají předponu test_

Základním rozdílem v knihovně Pythonu unittest je to, že není potřeba dědičnost.

Následující příklad používá tyto předpony a další zásady vytváření názvů Pythonu pro třídy a metody. Ukazuje malou testovací třídu, která kontroluje uživatelská jména v aplikaci.

class TestUser:

    def test_username(self):
        assert default() == "default username"

Spouštění testů

Pytest je testovací architektura i spouštěč testů. Spouštěč testů je spustitelný soubor na příkazovém řádku, který může na vysoké úrovni:

  • Proveďte kolekci testů vyhledáním všech testovacích souborů, tříd testů a testovacích funkcí pro testovací běh.
  • Spuštěním všech testů zahajte spuštění testu.
  • Sledujte chyby, chyby a úspěšné testy.
  • Poskytuje bohaté sestavy na konci testovacího běhu.

Poznámka:

Vzhledem k tomu, že Pytest je externí knihovna, musí být nainstalovaná, aby ji bylo možné použít.

Vzhledem k těmto obsahům v souboru test_main.py vidíme, jak se Pytest chová při spouštění testů:

# contents of test_main.py file

def test_main():
    assert True

Na příkazovém řádku ve stejné cestě , kde existuje soubor test_main.py , můžeme spustitelný pytest soubor:

 $ pytest
=========================== test session starts ============================
platform -- Python 3.10.1, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /private/tmp/project
collected 1 item

test_main.py .                                                       [100%]

============================ 1 passed in 0.00s =============================

Pytest na pozadí shromažďuje ukázkový test v testovacím souboru bez nutnosti konfigurace.

Výkonný příkaz assert

Zatím naše testovací příklady používají prostý assert volání. V Pythonu assert se příkaz obvykle nepoužívá pro testy, protože v případě selhání kontrolního výrazu chybí správné hlášení. Pytest ale toto omezení nemá. Pytest na pozadí umožňuje příkazu provádět bohaté porovnání bez vynucení, aby uživatel napsal více kódu nebo cokoli nakonfiguruje.

Pomocí prostého assert příkazu můžete použít operátory Pythonu. Například: >, , <!=, >=nebo <=. Všechny operátory Pythonu jsou platné. Tato funkce může být jedinou nejdůležitější funkcí Pytestu: nemusíte se učit novou syntaxi pro psaní kontrolních výrazů.

Pojďme se podívat, jak se to přeloží při řešení běžných porovnání s objekty Pythonu. V tomto případě si projdeme sestavu selhání při porovnávání dlouhých řetězců:

================================= FAILURES =================================
____________________________ test_long_strings _____________________________

    def test_long_strings():
        left = "this is a very long strings to be compared with another long string"
        right = "This is a very long string to be compared with another long string"
>       assert left == right
E       AssertionError: assert 'this is a ve...r long string' == 'This is a ve...r long string'
E         - This is a very long string to be compared with another long string
E         ? ^
E         + this is a very long strings to be compared with another long string
E         ? ^                         +

test_main.py:4: AssertionError

Pytest ukazuje užitečný kontext kolem selhání. Nesprávná velikost písmen na začátku řetězce a nadbytečný znak ve slově. Kromě řetězců může Pytest pomoct s jinými objekty a datovými strukturami. Tady je příklad, jak se chová se seznamy:

________________________________ test_lists ________________________________

    def test_lists():
        left = ["sugar", "wheat", "coffee", "salt", "water", "milk"]
        right = ["sugar", "coffee", "wheat", "salt", "water", "milk"]
>       assert left == right
E       AssertionError: assert ['sugar', 'wh...ater', 'milk'] == ['sugar', 'co...ater', 'milk']
E         At index 1 diff: 'wheat' != 'coffee'
E         Full diff:
E         - ['sugar', 'coffee', 'wheat', 'salt', 'water', 'milk']
E         ?                     ---------
E         + ['sugar', 'wheat', 'coffee', 'salt', 'water', 'milk']
E         ?           +++++++++

test_main.py:9: AssertionError

Tato sestava identifikuje, že index 1 (druhá položka v seznamu) se liší. Nejen že identifikuje číslo indexu, ale také představuje selhání. Kromě porovnání položek může také hlásit, jestli položky chybí, a poskytnout informace, které vám můžou přesně říct, které položky by mohly být. V následujícím případě to znamená "milk":

________________________________ test_lists ________________________________

    def test_lists():
        left = ["sugar", "wheat", "coffee", "salt", "water", "milk"]
        right = ["sugar", "wheat", "salt", "water", "milk"]
>       assert left == right
E       AssertionError: assert ['sugar', 'wh...ater', 'milk'] == ['sugar', 'wh...ater', 'milk']
E         At index 2 diff: 'coffee' != 'salt'
E         Left contains one more item: 'milk'
E         Full diff:
E         - ['sugar', 'wheat', 'salt', 'water', 'milk']
E         + ['sugar', 'wheat', 'coffee', 'salt', 'water', 'milk']
E         ?                    ++++++++++

test_main.py:9: AssertionError

Nakonec se podíváme, jak se chová se slovníky. Porovnánídvouchm kódům může dojít, pokud dojde k selhání, ale Pytest provede nevyřízenou úlohu při poskytování kontextu a určení selhání:

____________________________ test_dictionaries _____________________________

    def test_dictionaries():
        left = {"street": "Ferry Ln.", "number": 39, "state": "Nevada", "zipcode": 30877, "county": "Frett"}
        right = {"street": "Ferry Lane", "number": 38, "state": "Nevada", "zipcode": 30877, "county": "Frett"}
>       assert left == right
E       AssertionError: assert {'county': 'F...rry Ln.', ...} == {'county': 'F...ry Lane', ...}
E         Omitting 3 identical items, use -vv to show
E         Differing items:
E         {'street': 'Ferry Ln.'} != {'street': 'Ferry Lane'}
E         {'number': 39} != {'number': 38}
E         Full diff:
E           {
E            'county': 'Frett',...
E
E         ...Full output truncated (12 lines hidden), use '-vv' to show

V tomto testu existují ve slovníku dvě selhání. Jednou z nich je to, že "street" se hodnota liší a druhá hodnota "number" se neshoduje.

Pytest přesně rozpoznává tyto rozdíly (i když se jedná o jedno selhání v jednom testu). Vzhledem k tomu, že slovníky obsahují mnoho položek, Pytest vynechá stejné části a zobrazí pouze relevantní obsah. Pojďme se podívat, co se stane, když použijeme navrhovaný -vv příznak ke zvýšení podrobností ve výstupu:

____________________________ test_dictionaries _____________________________

    def test_dictionaries():
        left = {"street": "Ferry Ln.", "number": 39, "state": "Nevada", "zipcode": 30877, "county": "Frett"}
        right = {"street": "Ferry Lane", "number": 38, "state": "Nevada", "zipcode": 30877, "county": "Frett"}
>       assert left == right
E       AssertionError: assert {'county': 'Frett',\n 'number': 39,\n 'state': 'Nevada',\n 'street': 'Ferry Ln.',\n 'zipcode': 30877} == {'county': 'Frett',\n 'number': 38,\n 'state': 'Nevada',\n 'street': 'Ferry Lane',\n 'zipcode': 30877}
E         Common items:
E         {'county': 'Frett', 'state': 'Nevada', 'zipcode': 30877}
E         Differing items:
E         {'number': 39} != {'number': 38}
E         {'street': 'Ferry Ln.'} != {'street': 'Ferry Lane'}
E         Full diff:
E           {
E            'county': 'Frett',
E         -  'number': 38,
E         ?             ^
E         +  'number': 39,
E         ?             ^
E            'state': 'Nevada',
E         -  'street': 'Ferry Lane',
E         ?                    - ^
E         +  'street': 'Ferry Ln.',
E         ?                     ^
E            'zipcode': 30877,
E           }

Spuštěním pytest -vvsestav zvyšuje množství podrobností a poskytuje podrobné porovnání. Tato sestava nejen detekuje a zobrazuje selhání, ale umožňuje rychle provádět změny pro nápravu problému.