Тестування класів і методів

Завершено

Окрім функції перевірки письма, Pytest дає змогу використовувати заняття. Як ми вже згадували, успадкування не потрібне, а тестові класи дотримуються кількох простих правил. Використання класів забезпечує більшу гнучкість і повторне використання. Як ви бачите далі, Pytest продовжує йти з дороги і не змушує вас писати тести певним чином.

Так само, як і функції, ви все одно можете писати твердження за допомогою assert оператора.

Створення тестового класу

Давайте скористаємося реальним сценарієм, щоб дізнатися, як можуть допомогти тестові класи. Наведена нижче функція перевіряє, чи містить певний файл "так" у вмісті. Якщо так, повертається Trueзначення . Якщо файл не існує або містить "ні" в його вмісті, він повертає .False Цей сценарій поширений в асинхронних завданнях, які використовують файлову систему для позначення завершення.

Ось як виглядає функція:

import os

def is_done(path):
    if not os.path.exists(path):
        return False
    with open(path) as _f:
        contents = _f.read()
    if "yes" in contents.lower():
        return True
    elif "no" in contents.lower():
        return False

Ось як виглядає клас із двома тестами (по одному для кожної умови) у файлі з іменем test_files.py .

class TestIsDone:

    def test_yes(self):
        with open("/tmp/test_file", "w") as _f:
            _f.write("yes")
        assert is_done("/tmp/test_file") is True

    def test_no(self):
        with open("/tmp/test_file", "w") as _f:
            _f.write("no")
        assert is_done("/tmp/test_file") is False

Застереження

Методи перевірки використовують шлях /tmp для тимчасового тестового файлу, тому що його простіше використовувати для прикладу. Однак, якщо вам потрібно використовувати тимчасові файли, радимо використовувати бібліотеку, подібну tempfile до цієї, може безпечно створити (і видалити) їх для вас. Не в кожній системі є каталог /tmp , і це розташування може бути не тимчасовим залежно від операційної системи.

Виконання тестів із позначкою -v для підвищення словесного наповнення показує, що тести проходять:

pytest -v test_files.py
============================= test session starts ==============================
Python 3.9.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 
cachedir: .pytest_cache
rootdir: /private/
collected 2 items

test_files.py::TestIsDone::test_yes PASSED                               [ 50%]
test_files.py::TestIsDone::test_no PASSED                                [100%]

============================== 2 passed in 0.00s ===============================

Хоча тести проходять, вони виглядають повторюваними, і вони також залишають файли після перевірки. Перш ніж ми побачимо, як ми можемо покращити їх, давайте розглянемо методи підтримки в наступному розділі.

Допоміжні методи

У тестовому класі є кілька способів, за допомогою яких можна настроїти й розірвати виконання тесту. Pytest автоматично виконує їх, якщо їх визначено. Щоб використовувати ці методи, слід знати, що вони мають певний порядок і поведінку.

  • setup: виконується один раз перед кожною перевіркою в класі.
  • teardown: виконується один раз після кожного тесту в класі.
  • setup_class: виконується один раз перед усіма тестами в класі.
  • teardown_class: виконується один раз після всіх тестів у класі.

Коли для перевірки потрібні схожі (або ідентичні) ресурси, корисно писати методи настроювання. В ідеалі, тест не повинен залишати ресурси, коли він завершується, тому методи розриву можуть допомогти в очищенні тесту в таких ситуаціях.

Очищення

Давайте розглянемо оновлений тестовий клас, який очищає файли після кожної перевірки:

class TestIsDone:

    def teardown(self):
        if os.path.exists("/tmp/test_file"):
            os.remove("/tmp/test_file")

    def test_yes(self):
        with open("/tmp/test_file", "w") as _f:
            _f.write("yes")
        assert is_done("/tmp/test_file") is True

    def test_no(self):
        with open("/tmp/test_file", "w") as _f:
            _f.write("no")
        assert is_done("/tmp/test_file") is False

Оскільки ми використовували teardown() цей метод, цей тестовий клас більше не залишає /tmp/test_file позаду.

Налаштування

Ще одне вдосконалення, яке ми можемо внести до цього класу, – додати змінну, яка вказує на файл. Оскільки файл оголошено в шести розташуваннях, будь-які зміни, внесені до шляху, означатиме зміну його в усіх цих місцях. У цьому прикладі показано, як виглядає клас за допомогою додаткового setup() методу, який оголошує змінну шляху:

class TestIsDone:

    def setup(self):
        self.tmp_file = "/tmp/test_file"

    def teardown(self):
        if os.path.exists(self.tmp_file):
            os.remove(self.tmp_file)

    def test_yes(self):
        with open(self.tmp_file, "w") as _f:
            _f.write("yes")
        assert is_done(self.tmp_file) is True

    def test_no(self):
        with open(self.tmp_file, "w") as _f:
            _f.write("no")
        assert is_done(self.tmp_file) is False

Настроювані методи підтримки

Ви можете створити спеціальні допоміжні методи в класі. Ці методи не мають префіксуватися з іменем test і не можуть називатися методами налаштування або очищення. У класі TestIsDone ми могли автоматизувати створення тимчасового файлу в спеціальному помічнику. Цей спеціальний спосіб підтримки може мати такий вигляд:

    def write_tmp_file(self, content):
        with open(self.tmp_file, "w") as _f:
            _f.write(content)

Функція Pytest не виконує write_tmp_file() метод автоматично, а інші методи можуть викликати його безпосередньо для збереження на повторюваних завданнях, таких як запис у файл.

Увесь клас має такий вигляд:

class TestIsDone:

    def setup(self):
        self.tmp_file = "/tmp/test_file"

    def teardown(self):
        if os.path.exists(self.tmp_file):
            os.remove(self.tmp_file)

    def write_tmp_file(self, content):
        with open(self.tmp_file, "w") as _f:
            _f.write(content)

    def test_yes(self):
        self.write_tmp_file("yes")
        assert is_done(self.tmp_file) is True

    def test_no(self):
        self.write_tmp_file("no")
        assert is_done(self.tmp_file) is False

Використання класу замість функції

Немає суворих правил використання класу замість функції. Завжди варто дотримуватися умов у поточних проектах і командах, з якими ви працюєте. Ось кілька загальних запитань, які допоможуть визначити, коли слід використовувати клас.

  • Чи потрібні ваші тести подібним кодом довідки з налаштування або очищення?
  • Чи має логічний сенс групування тестів?
  • Чи є в тестовому комплексі принаймні кілька тестів?
  • Чи можуть ваші тести скористатися загальним набором допоміжних функцій?