Практическое тестирование Python-приложений: алгоритмы и модули

Раздел: Разработка на Python -> тестирование

Тестирование алгоритмов и программ на Python - неотъемлемая часть разработки, позволяющая убедиться в корректности, производительности и устойчивости кода. В статье рассмотрены различные подходы к тестированию: от простых модульных тестов до property‑based тестирования и замеров производительности.

Основные подходы к тестированию

Как написать простой модульный тест для алгоритма сортировки с помощью pytest?

Решение: pytest - современный и гибкий фреймворк для тестирования. Установите его через pip install pytest, создайте файл test_sort.py и напишите тестовую функцию.


# test_sort.py
def sort_list(arr):
    return sorted(arr)

def test_sort_list():
    assert sort_list([3, 1, 2]) == [1, 2, 3]
    assert sort_list([]) == []
    assert sort_list([5]) == [5]

A b test python (a/b тестирование в python)

Запустите тесты командой pytest test_sort.py. Результат:

collected 1 item
test_sort.py .                                              [100%]
1 passed in 0.01s

тесты алгоритмы и программирование python (тестирование алгоритмов и программ на python)

Типичные ошибки: отсутствие проверки граничных случаев (пустой список, один элемент, дубликаты, отрицательные числа). Решение - добавлять соответствующие тесты. Также возможны ошибки импорта, если файл лежит не в корне.

Как использовать unittest для тестирования классов?

Решение: встроенный модуль unittest предоставляет класс TestCase. Создайте подкласс и определите методы test_*.


import unittest

def multiply(a, b):
    return a * b

class TestMath(unittest.TestCase):
    def test_multiply(self):
        self.assertEqual(multiply(3, 4), 12)
        self.assertEqual(multiply(-1, 5), -5)

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

Python code tests (тестирование кода в python)

Типичные ошибки: забывание вызывать assertEqual вместо assert (хотя можно и assert, но теряется читаемость). Также необходимо запускать через python -m unittest или main().

Как встроить тесты в документацию функции с помощью doctest?

Решение: записывайте примеры использования в docstring в формате интерактивной сессии. Модуль doctest автоматически проверяет их.


def factorial(n):
    """
    Вычисляет факториал числа n.
    >>> factorial(5)
    120
    >>> factorial(0)
    1
    >>> factorial(-1)
    Traceback (most recent call last):
        ...
    ValueError: n must be non-negative
    """
    if n < 0:
        raise ValueError("n must be non-negative")
    return 1 if n == 0 else n * factorial(n-1)

if __name__ == "__main__":
    import doctest
    doctest.testmod()

Py test python (написание тестов на python (pytest))

Типичные ошибки: несоответствие пробелов в ожидаемом выводе; забывание импортировать doctest. Для сложных тестов doctest неудобен из-за громоздких docstring.

Как автоматически генерировать тестовые данные для проверки свойств алгоритма?

Решение: библиотека hypothesis генерирует случайные тестовые случаи по стратегиям. Тестируются не конкретные значения, а свойства (property).


from hypothesis import given, strategies as st
from reverse import reverse  # reverse(s) возвращает перевёрнутую строку

@given(st.text())
def test_reverse_property(s):
    assert reverse(reverse(s)) == s

Test data python (создание тестовых данных в python)

Falsifying example: test_reverse_property(s='\x00')

Типичные ошибки: слишком общие стратегии могут генерировать неинтересные данные (пустая строка). Нужно настраивать стратегии. Также возможны бесконечные циклы, если свойство нарушено.

Как измерить время выполнения алгоритма и убедиться в его эффективности?

Решение: используйте модуль timeit или pytest‑benchmark. Сравните несколько реализаций.


import timeit

def bubble_sort(arr):
    for i in range(len(arr)):
        for j in range(len(arr)-i-1):
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]
    return arr

def quick_sort(arr):
    if len(arr) <= 1:
        return arr
    pivot = arr[0]
    left = [x for x in arr[1:] if x <= pivot]
    right = [x for x in arr[1:] if x > pivot]
    return quick_sort(left) + [pivot] + quick_sort(right)

# Замеры
t_bubble = timeit.timeit(lambda: bubble_sort([5,3,1,4,2]), number=10000)
t_quick = timeit.timeit(lambda: quick_sort([5,3,1,4,2]), number=10000)
print(f"Bubble: {t_bubble:.4f}s, Quick: {t_quick:.4f}s")
Bubble: 0.4523s, Quick: 0.0876s

Типичные ошибки: однократный замер не показателен; нужно многократное повторение. Разные списки могут давать разное время.

Как тестировать функции, обращающиеся к внешним API или базе данных?

Решение: используйте моки (unittest.mock) для подмены внешних вызовов.


from unittest.mock import patch
import requests

def fetch_data(url):
    response = requests.get(url)
    return response.json()

def test_fetch_data():
    with patch('requests.get') as mock_get:
        mock_get.return_value.json.return_value = {'key': 'value'}
        result = fetch_data('http://fake.url')
        assert result == {'key': 'value'}

Типичные ошибки: неправильный путь для patch (используйте полное имя импортируемого объекта). Также забывают проверить, что вызов действительно произошёл.

Как проверить, что функция выбрасывает правильное исключение?

Решение: в pytest используйте контекстный менеджер pytest.raises.


import pytest

def divide(a, b):
    if b == 0:
        raise ZeroDivisionError("division by zero")
    return a / b

def test_divide_by_zero():
    with pytest.raises(ZeroDivisionError, match="division by zero"):
        divide(10, 0)

Типичные ошибки: не указан match или указан неверный; использование простого try/except вместо менеджера.

Расширенные примеры тестирования

Приведённые ниже примеры демонстрируют более сложные сценарии, часто встречающиеся в реальной разработке.

Параметризованные тесты с pytest

Параметризация позволяет запустить один тест с разными входными данными без дублирования кода.

Пример

import pytest

def is_even(n):
    return n % 2 == 0

@pytest.mark.parametrize("n,expected", [
    (2, True),
    (3, False),
    (0, True),
    (-2, True),
    (-3, False),
])
def test_is_even(n, expected):
    assert is_even(n) == expected
collected 5 items
test_param.py .....                                         [100%]
5 passed in 0.01s

Тестирование асинхронного кода с pytest-asyncio

Для асинхронных функций используйте плагин pytest-asyncio.

Пример

import pytest
import asyncio

async def fetch_data_async():
    await asyncio.sleep(0.1)
    return "data"

@pytest.mark.asyncio
async def test_fetch_data_async():
    result = await fetch_data_async()
    assert result == "data"
collected 1 item
test_async.py .                                             [100%]
1 passed in 0.11s

Мокирование внешнего сервиса с unittest.mock

Подмена сложного внешнего вызова, возвращающего итератор или контекстный менеджер.

Пример

from unittest.mock import Mock, patch

class Database:
    def connect(self):
        return "real connection"

def get_data(db):
    conn = db.connect()
    return f"data from {conn}"

def test_get_data():
    mock_db = Mock()
    mock_db.connect.return_value = "mock connection"
    result = get_data(mock_db)
    assert result == "data from mock connection"

Тестирование с hypothesis для сортировки

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

Пример

from hypothesis import given, strategies as st

def sort_list(arr):
    return sorted(arr)

@given(st.lists(st.integers()))
def test_sort_is_sorted_and_permutation(arr):
    result = sort_list(arr)
    # Проверка, что результат отсортирован
    assert all(result[i] <= result[i+1] for i in range(len(result)-1))
    # Проверка, что результат является перестановкой (одинаковое количество элементов)
    assert sorted(result) == result  # нестрого, но для сортировки верно
    # Более точная проверка: сравнение мультимножеств со входом
    from collections import Counter
    assert Counter(result) == Counter(arr)
collected 1 item
test_hypothesis_sort.py .                                  [100%]
1 passed in 0.05s

тестирование алгоритмов и программ на Python - comments

En
тесты алгоритмы и программирование python (python)