unittest.mock
или pytest-mock
. Позволяют контролировать ввод/вывод и избежать реальных операций.Функции с побочными эффектами, такие как те, которые работают с файловой системой, базой данных или отправляют сетевые запросы, требуют особого подхода при тестировании, чтобы обеспечить изолированность и предсказуемость тестов. Вместо тестирования непосредственно реальных побочных эффектов, мы используем методы для замены (заглушки) или перенаправления этих эффектов в контролируемую среду. Вот несколько стратегий:
unittest.mock
(или pytest-mock
) в Python предоставляют инструменты для создания моков.
import unittest
from unittest.mock import patch
import my_module # Модуль с функцией, которую мы хотим протестировать
def function_with_side_effect(filename):
with open(filename, 'w') as f:
f.write("Some data")
return "File written"
class TestMyModule(unittest.TestCase):
@patch('my_module.open', create=True) # Заменяем встроенную функцию open моком
def test_function_with_side_effect(self, mock_open):
# Настраиваем поведение мока (если необходимо)
mock_file = mock_open.return_value
mock_file.write.return_value = None # Например, если функция возвращает результат write
# Вызываем функцию, которую мы хотим протестировать
result = function_with_side_effect("test.txt")
# Проверяем, что open была вызвана с ожидаемым аргументом
mock_open.assert_called_with("test.txt", 'w')
# Проверяем, что метод write был вызван
mock_file.write.assert_called_with("Some data")
# Проверяем возвращаемое значение функции
self.assertEqual(result, "File written")
pytest
) используются для подготовки тестовой среды и данных перед выполнением теста, а также для очистки после его завершения. Например, можно создать временный файл или базу данных, которые будут доступны только для данного теста.
import pytest
import os
@pytest.fixture
def temp_file():
# Setup: Создаем временный файл
filename = "temp_test_file.txt"
with open(filename, "w") as f:
f.write("Initial data")
yield filename # Передаем имя файла тесту
# Teardown: Удаляем временный файл после теста
os.remove(filename)
def test_function_using_temp_file(temp_file):
# temp_file содержит имя временного файла
with open(temp_file, "r") as f:
content = f.read()
assert content == "Initial data"
with open(temp_file, "w") as f:
f.write("Modified data")
with open(temp_file, "r") as f:
content = f.read()
assert content == "Modified data"
def function_with_db_dependency(db_connection, data):
# Используем db_connection для работы с базой данных
db_connection.execute("INSERT INTO table VALUES (?)", (data,))
# В тестовом коде:
mock_db_connection = Mock()
function_with_db_dependency(mock_db_connection, "test_data")
mock_db_connection.execute.assert_called_with("INSERT INTO table VALUES (?)", ("test_data",))
pytest.mark.parametrize
) позволяет запускать один и тот же тест с разными входными данными и ожидаемыми результатами, что особенно полезно для проверки различных сценариев, связанных с побочными эффектами.
Важные соображения:
open('file.txt', 'w')
). Вместо этого используйте внедрение зависимостей или стратегии, описанные выше.Выбор конкретной стратегии зависит от сложности и контекста тестируемой функции.