Testování jednotek pomocí modulu unittest Pythonu

Žádný zodpovědný programátor neimplementuje kód bez důkladného ověření. Testování jednotek je proces ověřování jednotlivých komponent rozsáhlého softwarového projektu.

Tento článek se zaměřuje na to, jak můžete realizovat testování jednotlivých úseků kódu za pomoci modulu unittest v Pythonu. Nejprve si objasněme, jaké existují druhy testování.

V oblasti testování rozlišujeme manuální a automatické testování. Manuální testování představuje techniku, kdy lidé ručně provádějí testy po dokončení vývoje. Automatické testování je technika, při které programy samy provádějí testy a poskytují nám výsledky.

Je zřejmé, že manuální testování je časově náročné a obtížné na provedení. Proto vývojáři vytvářejí kód pro automatické provádění testů (automatizované testování). V automatizovaném testování existuje několik druhů testování. Mezi ně patří testování jednotek, integrační testování, komplexní testování, zátěžové testování a další.

Pojďme si projít standardní postup testování.

  • Vytvořte nebo upravte kód.
  • Napište nebo upravte testy pro různé scénáře vašeho kódu.
  • Spusťte testy (manuálně nebo pomocí testovacího nástroje).
  • Analyzujte výsledky testů. Pokud se objeví nějaké chyby, opravte je a opakujte kroky.

V tomto textu se budeme zabývat nejzákladnějším typem testování, tedy testováním jednotek. Bez dalších okolků se ponořme do samotného návodu.

Co je to Testování Jednotek?

Testování jednotek je technika pro ověřování malého, nezávislého bloku kódu. Tímto malým blokem kódu bývá ve většině případů funkce. Výraz nezávislý znamená, že tato část kódu není závislá na jiných částech projektu.

Řekněme, že potřebujeme ověřit, zda se řetězec rovná „etechblog.cz“. Pro tento účel jsme napsali funkci, která přijímá jeden argument a vrací, zda se rovná „etechblog.cz“ nebo ne.

def is_equal_to_geekflare(string):
	return string == "etechblog.cz"

Tato funkce není závislá na žádném jiném kódu. Můžeme ji tedy nezávisle otestovat zadáváním různých vstupů. Takový nezávislý úsek kódu je možné využít v celém projektu.

Důležitost Testování Jednotek

Obecně platí, že kód nezávislých bloků se často využívá v celém projektu. Proto je zásadní, aby byl dobře napsaný a otestovaný. Testy jednotek slouží k ověřování těchto nezávislých bloků kódu. Co se stane, pokud pro náš projekt testování jednotek neaplikujeme?

Představme si, že jsme neotestovali malé bloky kódu, které se používají napříč projektem. V takovém případě mohou selhat všechny ostatní testy, jako jsou integrační testy, komplexní testy atd., které tyto malé bloky kódu využívají. To může vést k narušení funkčnosti aplikace. Proto je klíčové důkladně otestovat základní stavební kameny kódu.

Nyní už chápeme, jak důležité je testování jednotek a vytváření testů pro všechny nezávislé bloky kódu. Díky tomu, že provádíme testy jednotek, předejdeme selhání dalších testů (integrační, komplexní testy atd.) kvůli chybám v nezávislých blocích kódu.

V následujících oddílech si představíme, co je modul unittest v Pythonu a jak jej můžeme použít pro psaní testů jednotek v Pythonu.

Poznámka: Předpokládáme, že jste obeznámeni s koncepty tříd, modulů atd. v Pythonu. Pokud neovládáte pokročilé koncepty Pythonu, může být pro vás složité porozumět dalším částem.

Co je unittest v Pythonu?

Python unittest je integrovaný testovací framework pro ověřování kódu v Pythonu. Obsahuje testovací nástroj, který nám umožňuje spouštět testy bez velkého úsilí. K testování můžeme využít vestavěný modul unittest a obejít se bez modulů třetích stran. Samozřejmě se to odvíjí od vašich potřeb. Integrovaný modul unittest je dobrou startovní pozicí pro testování v Pythonu.

Při testování kódu v Pythonu s modulem unittest je nutné dodržovat následující kroky.

#1. Napište kód.

#2. Importujte modul unittest.

#3. Vytvořte soubor, který začíná klíčovým slovem test. Například test_prime.py. Klíčové slovo test slouží k identifikaci testovacích souborů.

#4. Vytvořte třídu, která dědí od třídy unittest.TestCase.

#5. Uvnitř třídy napište metody (testy). Každá metoda obsahuje různé testovací scénáře podle vašich potřeb. Metodu je nutné pojmenovat tak, aby začínala klíčovým slovem test.

#6. Spusťte testy. Testy lze spouštět různými způsoby.

  • Spusťte příkaz python -m unittest název_souboru.py.
  • Spusťte testovací soubory jako běžné Python soubory pomocí příkazu python název_souboru.py. Aby tato metoda fungovala, musíme v testovacím souboru zavolat hlavní metodu unittestu.
  • A nakonec, pomocí automatického vyhledávání. Můžeme automaticky spouštět testy příkazem python -m unittest discover bez explicitního uvedení názvu testovacího souboru. Program automaticky vyhledá testy podle konvence pojmenování, kterou jsme dodrželi. Je tedy nutné, aby názvy našich testovacích souborů obsahovaly klíčové slovo test.

Obvykle při testování porovnáváme výstup kódu s očekávaným výstupem. K porovnávání výstupů nabízí unittest různé metody. Seznam porovnávacích funkcí naleznete zde.

Jejich princip je jednoduchý a snadno pochopitelný.

Tolik teorie. Nyní se musíme pustit do kódování.

Poznámka: Pokud máte jakékoli otázky ohledně modulu unittest, můžete se podívat do dokumentace a vyjasnit si své nejasnosti. Bez dalšího zdržování začněme modul unittest používat.

Testy Jednotek v Pythonu pomocí unittest

Nejprve napíšeme nějaké funkce a poté se zaměříme na tvorbu testů. Otevřete složku ve vašem oblíbeném textovém editoru. A vytvořte soubor s názvem utils.py. Vložte následující kód do tohoto souboru.

import math


def is_prime(n):
    if n < 0:
        return 'Záporná čísla nejsou povolena'

    if n <= 1:
        return False

    if n == 2:
        return True

    if n % 2 == 0:
        return False

    for i in range(2, int(math.sqrt(n)) + 1):
        if n % i == 0:
            return False
    return True


def cubic(a):
    return a * a * a


def say_hello(name):
    return "Ahoj, " + name

V souboru utils.py máme tři různé funkce. Nyní potřebujeme otestovat každou funkci v různých testovacích případech. Pojďme napsat testy pro první funkci is_prime.

#1. Ve složce s projektem vytvořte soubor s názvem test_utils.py, podobně jako utils.py.

#2. Importujte modul utils a unittest.

#3. Vytvořte třídu s názvem TestUtils, která dědí od třídy unittest.TestCase. Název třídy může být libovolný, zkuste ale, aby byl smysluplný.

#4. Uvnitř třídy napište metodu s názvem test_is_prime, která přijímá argument self.

#5. Napište různé testovací scénáře s argumenty pro funkci is_prime a porovnejte výstup s očekávaným výstupem.

#6. Příklad testovacího scénáře: self.assertFalse(utils.is_prime(1)).

#7. V tomto případě očekáváme, že výstup funkce is_prime(1) bude false.

#8. Stejným způsobem otestujeme další scénáře na základě funkce, kterou ověřujeme.

Podívejme se na testy.

import unittest

import utils


class TestUtils(unittest.TestCase):
    def test_is_prime(self):
        self.assertFalse(utils.is_prime(4))
        self.assertTrue(utils.is_prime(2))
        self.assertTrue(utils.is_prime(3))
        self.assertFalse(utils.is_prime(8))
        self.assertFalse(utils.is_prime(10))
        self.assertTrue(utils.is_prime(7))
        self.assertEqual(utils.is_prime(-3),
                         "Záporná čísla nejsou povolena")


if __name__ == '__main__':
    unittest.main()

Voláním hlavní metody modulu unittest spustíme testy příkazem python název_souboru.py. Nyní spusťte testy.

Uvidíte výstup podobný tomu níže.

$ python test_utils.py
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK

Nyní se pokuste vytvořit testovací scénáře i pro další funkce. Vymyslete různé scénáře a napište pro ně testy. Podívejte se na následující testy, které jsou přidány do výše uvedené třídy.

...


class TestUtils(unittest.TestCase):
    def test_is_prime(self):
        ...

    def test_cubic(self):
        self.assertEqual(utils.cubic(2), 8)
        self.assertEqual(utils.cubic(-2), -8)
        self.assertNotEqual(utils.cubic(2), 4)
        self.assertNotEqual(utils.cubic(-3), 27)

    def test_say_hello(self):
        self.assertEqual(utils.say_hello("etechblog.cz"), "Ahoj, etechblog.cz")
        self.assertEqual(utils.say_hello("Chandan"), "Ahoj, Chandan")
        self.assertNotEqual(utils.say_hello("Chandan"), "Hi, Chandan")
        self.assertNotEqual(utils.say_hello("Hafeez"), "Hi, Hafeez")


...

Využili jsme jen několik porovnávacích funkcí z modulu unittest. Kompletní seznam naleznete zde.

Naučili jsme se, jak psát testy jednotek pomocí modulu unittest. Nyní se podíváme na různé způsoby, jak testy spouštět.

Jak spouštět testy pomocí unittest

Výše jsme si již ukázali způsob, jak spouštět testovací scénáře. Podívejme se na další dva způsoby spouštění testů pomocí modulu unittest.

#1. Pomocí názvu souboru a modulu unittest.

V tomto případě pro spuštění testů využijeme modul unittest a název souboru. Příkaz pro spuštění testů je python -m unittest název_souboru.py. V našem případě je příkaz python -m unittest test_utils.py.

#2. Pomocí metody discover

Pro automatickou detekci všech testovacích souborů a jejich spuštění použijeme metodu discover modulu unittest. Abychom mohli automaticky detekovat testovací soubory, musí jejich názvy začínat klíčovým slovem test.

Příkaz pro spuštění testů metodou discover je python -m unittest discover. Příkaz detekuje všechny soubory, jejichž názvy začínají na test, a spustí je.

Závěr 👩‍💻

Testy jednotek jsou základním typem testování ve světě programování. V reálném světě existuje mnoho dalších testů. Zkuste se je naučit postupně. Doufám, že vám tento návod pomůže napsat základní testy v Pythonu pomocí modulu unittest. Existují knihovny třetích stran, jako je pytest, Robot Framework, nose, nose2, slash atd. Můžete je prozkoumat podle potřeb vašeho projektu.

Příjemné testování 😎

Mohly by vás také zajímat Otázky a odpovědi pro pohovory v Pythonu.