Использовал ли ты контекстные менеджеры в Python для тестирования?

Ответ

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

Основные сценарии применения в тестировании:

  1. Работа с временными файлами и данными: Использую tempfile.TemporaryDirectory и tempfile.NamedTemporaryFile, чтобы файлы удалялись автоматически после теста, даже если он упал.

    import tempfile
    import json
    
    def test_data_is_saved_correctly():
        with tempfile.NamedTemporaryFile(mode='w+', suffix='.json', delete=False) as tmp:
            # Тест записывает данные во временный файл
            json.dump({"test": "data"}, tmp)
            tmp.flush()
            # ... здесь код, который читает и проверяет файл
        # Файл автоматически удаляется после выхода из блока `with`
  2. Мокирование и патчинг (с помощью unittest.mock): Контекстный менеджер patch позволяет временно подменить объект только на время выполнения теста.

    from unittest.mock import patch
    
    def test_external_api_call():
        with patch('requests.get') as mock_get:
            # Настраиваем мок на время этого теста
            mock_get.return_value.status_code = 200
            mock_get.return_value.json.return_value = {"ok": true}
    
            # Вызов тестируемой функции, которая использует requests.get
            result = function_under_test()
            assert result is True
            # После выхода из `with` оригинальный requests.get восстанавливается
  3. Управление соединениями с БД или внешними сервисами: Пишу собственные контекстные менеджеры или использую предоставляемые фреймворками (например, в Django/Pytest) для работы с тестовыми транзакциями, чтобы не засорять базу данных.

  4. Замер времени выполнения (для performance-тестов):

    import time
    from contextlib import contextmanager
    
    @contextmanager
    def timer():
        start = time.perf_counter()
        yield
        end = time.perf_counter()
        print(f"Time elapsed: {end - start:.4f} seconds")
    
    def test_function_performance():
        with timer():
            # Выполняем операцию, время которой хотим замерить
            heavy_operation()

Использование with гарантирует, что ресурсы будут освобождены, а состояние окружения — восстановлено, что делает тесты более надежными и идемпотентными.

Ответ 18+ 🔞

А, ну это же классика, ебать мои старые костыли! Контекстные менеджеры в тестах — это как надежный друг, который всегда прикроет твою кривую жопу. Без них — просто пиздец, а не тесты, один сплошной распиздяй в коде.

Смотри, в чём тут соль. Представь, ты пишешь тест, который создаёт кучу временных файлов. Без with и tempfile ты либо забудешь их удалить, и твой диск превратится в помойку, либо тест упадёт на середине, и файлы так и останутся висеть, как манда с ушами. А с контекстным менеджером — всё чисто, даже если всё пошло по пизде. Выйдешь из блока with — и файлов как не бывало, хоть трава не расти.

import tempfile
import json

def test_data_is_saved_correctly():
    with tempfile.NamedTemporaryFile(mode='w+', suffix='.json', delete=False) as tmp:
        # Пишем всякую хуйню во временный файл
        json.dump({"test": "data"}, tmp)
        tmp.flush()
        # ... тут проверяем, что всё записалось
    # А тут уже файла нет, всё само подмели, красота!

Или вот ещё любимый фокус — мокирование. Ёпта, без patch из unittest.mock вообще никуда. Тебе же не нужно, чтобы твой тест реально лез в интернет или в продовскую базу? Вот и я о том же. Нахуй эти риски.

from unittest.mock import patch

def test_external_api_call():
    with patch('requests.get') as mock_get:
        # Говорим моку: "Слушай сюда, чувак, на время этого теста ты — requests.get"
        mock_get.return_value.status_code = 200
        mock_get.return_value.json.return_value = {"ok": true}

        # А тут наша функция дергает этот мок, даже не подозревая подмены
        result = function_under_test()
        assert result is True
        # Всё, тест кончился, мок снялся, и никто ничего не заметил. Идеальная операция.

А если тебе нужно что-то своё, хитрожопое? Например, засечь, сколько времени выполняется какая-нибудь тяжёлая хрень? Да запросто, свой контекстный менеджер напишешь за минуту.

import time
from contextlib import contextmanager

@contextmanager
def timer():
    start = time.perf_counter()
    yield
    end = time.perf_counter()
    print(f"Time elapsed: {end - start:.4f} seconds")

def test_function_performance():
    with timer():
        # Запускаем какую-нибудь дохуя сложную операцию
        heavy_operation()
    # И сразу видим в консоли, не обосралась ли она по времени

Короче, суть в чём. Контекстные менеджеры через with — это гарантия, что какой бы пиздец ни случился в тесте, ресурсы за собой приберут, соединения закроют, а моки снимут. Тесты становятся изолированными, как будто каждый работает в своём пузыре. А это, блядь, самое главное для спокойной жизни. Иначе один кривой тест засрёт состояние для всех остальных, и потом будешь неделю искать, почему всё падает. Доверия к таким тестам — ноль ебать.