Как использовать PyTest для модульного тестирования в Python

Когда вы пишете код на Python, важно убедиться, что ваш код работает так, как ожидается. Один из лучших способов сделать это — использовать модульные тесты, которые помогают вам проверить, работают ли маленькие части (или единицы) вашего кода правильно.

В этой статье мы научимся писать и запускать эффективные модульные тесты в Python, используя PyTest, один из самых популярных фреймворков для тестирования в Python.

Что такое модульные тесты?

Модульные тесты — это небольшие, простые тесты, которые сосредоточены на проверке одной функции или небольшой части кода. Они помогают убедиться, что ваш код работает так, как ожидается, и могут выявить ошибки на ранней стадии.

Модульные тесты можно писать для различных частей вашего кода, таких как функции, методы и даже классы. Писать модульные тесты — значит тестировать ваш код, не запуская всю программу.

Почему стоит использовать PyTest?

PyTest — это популярный фреймворк для тестирования в Python, который упрощает написание и запуск тестов.

Он прост в использовании и имеет множество полезных функций, таких как:

  • Он позволяет писать простые и понятные тестовые случаи.
  • Он предоставляет расширенные функции, такие как фикстуры, параметризованные тесты и плагины.
  • Он хорошо работает с другими инструментами и библиотеками для тестирования.
  • Он генерирует легко читаемые результаты тестов и отчеты.

Настройка PyTest в Linux

Прежде чем мы начнем писать тесты, нам нужно установить PyTest. Если у вас не установлен PyTest, вы можете установить его с помощью менеджера пакетов Python, называемого pip.

pip install pytest

После установки PyTest вы готовы начать писать тесты!

Написание вашего первого теста с PyTest

Давайте начнем с написания простой функции, а затем напишем для нее тест.

Шаг 1: Напишите простую функцию

Сначала давайте создадим функцию на Python, которую мы хотим протестировать. Допустим, у нас есть функция, которая складывает два числа:

# add.py
def add(a, b):
    return a + b

Это простая функция, которая принимает два числа a и b, складывает их и возвращает результат.

Шаг 2: Напишите тест для функции

Теперь давайте напишем тест для функции сложения. В PyTest тесты пишутся в отдельных файлах, которые обычно называются test_*.py, чтобы было легко идентифицировать тестовые файлы.

Создайте новый файл с именем test_add.py и напишите следующий тестовый код:

# test_add.py
from add import add

def test_add_numbers():
    assert add(2, 3) == 5
    assert add(-1, 1) == 0
    assert add(0, 0) == 0

Объяснение приведенного выше кода:

  • Мы импортируем функцию add из файла add.py.
  • Мы определяем тестовую функцию с именем test_add_numbers(). В PyTest функция теста должна начинаться со слова test_.
  • Внутри тестовой функции мы используем оператор assert, чтобы проверить, соответствует ли результат вызова функции add ожидаемому значению. Если условие в операторе assert истинно (True), тест проходит; в противном случае он не проходит.

Шаг 3: Запустите тест

Чтобы запустить тест, откройте терминал и перейдите в директорию, где находится ваш файл test_add.py, а затем выполните следующую команду:

pytest

PyTest автоматически найдет все тестовые файлы (те, которые начинаются с test_) и выполнит тесты внутри них. Если все работает правильно, вы должны увидеть вывод, подобный этому:

Verifying Python Code Functionality

Точка (.) указывает на то, что тест прошел. Если возникли какие-либо проблемы, PyTest покажет сообщение об ошибке.

Написание более сложных тестов

Теперь, когда мы знаем, как писать и запускать базовый тест, давайте рассмотрим некоторые более продвинутые функции PyTest.

Тестирование ожидаемых исключений

Иногда вы хотите протестировать, вызывает ли ваш код правильные исключения, когда что-то идет не так. Вы можете сделать это с помощью функции pytest.raises().

Предположим, мы хотим протестировать функцию, которая делит два числа. Мы хотим вызвать исключение, если второе число равно нулю (чтобы избежать ошибок деления на ноль).

Вот функция divide:

# divide.py
def divide(a, b):
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b

Теперь давайте напишем тест для этой функции, который проверяет, вызывается ли ValueError при попытке деления на ноль:

# test_divide.py
from divide import divide
import pytest

def test_divide_numbers():
    assert divide(10, 2) == 5
    assert divide(-10, 2) == -5
    assert divide(10, -2) == -5

def test_divide_by_zero():
    with pytest.raises(ValueError):
        divide(10, 0)

Объяснение кода:

  • Мы добавили новую тестовую функцию под названием test_divide_by_zero().
  • Внутри этой функции мы используем pytest.raises(ValueError), чтобы проверить, возникает ли ValueError, когда мы вызываем функцию деления с нулем в качестве второго аргумента.

Запустите тесты снова с помощью команды pytest. Если все работает правильно, вы должны увидеть следующий вывод:

Test Your Code with PyTest

Использование фикстур для настройки и очистки

В некоторых случаях вам может понадобиться установить определенные условия перед запуском ваших тестов или очистить после завершения тестов. PyTest предоставляет фикстуры для обработки этого.

Фикстура – это функция, которую вы можете использовать для настройки или разрушения условий для ваших тестов. Фикстуры часто используются для создания объектов или подключения к базам данных, которые необходимы для тестов.

Вот пример использования фикстуры для настройки временного каталога для тестирования операций с файлами:

# test_file_operations.py
import pytest
import os

@pytest.fixture
def temporary_directory():
    temp_dir = "temp_dir"
    os.mkdir(temp_dir)
    yield temp_dir  # This is where the test will run
    os.rmdir(temp_dir)  # Cleanup after the test

def test_create_file(temporary_directory):
    file_path = os.path.join(temporary_directory, "test_file.txt")
    with open(file_path, "w") as f:
        f.write("Hello, world!")
    
    assert os.path.exists(file_path)

Объяснение кода:

  • Мы определяем фикстуру под названием temporary_directory, которая создает временный каталог перед тестом и удаляет его после.
  • Тестовая функция test_create_file() использует эту фикстуру для создания файла в временном каталоге и проверки, существует ли файл.

Запустите тесты снова с помощью команды pytest. PyTest автоматически обнаружит и использует фикстуру.

Параметризуйте ваши тесты с помощью Pytest

Иногда вы хотите запустить один и тот же тест с разными входными данными. PyTest позволяет делать это легко с помощью параметризации.

Предположим, мы хотим протестировать нашу функцию add с несколькими парами чисел. Вместо того чтобы писать отдельные тестовые функции для каждой пары, мы можем использовать pytest.mark.parametrize, чтобы запустить один и тот же тест с разными входными данными.

# test_add.py
import pytest
from add import add

@pytest.mark.parametrize("a, b, expected", [
    (2, 3, 5),
    (-1, 1, 0),
    (0, 0, 0),
    (100, 200, 300)
])
def test_add_numbers(a, b, expected):
    assert add(a, b) == expected

Объяснение кода:

  • Мы используем декоратор pytest.mark.parametrize, чтобы определить несколько наборов входных данных (a, b и expected).
  • Тест function test_add_numbers() будет запускаться один раз для каждого набора входных данных.

Запустите тесты снова с помощью команды pytest, которая выполнит тест четыре раза, один раз для каждого набора входных данных.

Заключение

В этой статье мы научились писать и запускать эффективные модульные тесты в Python с использованием PyTest, чтобы рано находить ошибки и гарантировать, что ваш код работает так, как ожидается.

PyTest облегчает написание и запуск этих тестов, и благодаря своим мощным функциям вы можете справляться с более сложными потребностями в тестировании по мере того, как вы развиваетесь в своем пути с Python.

Source:
https://www.tecmint.com/unit-testing-python-code-with-pytest/