py.test — как использовать контекстный менеджер в funcarg/fixture

Тесно связаны: Есть ли в python хорошая идиома для использования контекстных менеджеров в настройке/разборке


У меня есть контекстный менеджер, который используется в тестах для исправления времени/часового пояса. Я хочу, чтобы это было в pytest funcarg (или приспособлении, мы используем pytest 2.2.3, но я могу перевести в обратном порядке). Я мог бы просто сделать это:

def pytest_funcarg__fixedTimezone(request):
    # fix timezone to match Qld, no DST to worry about and matches all
    # Eastern states in winter.
    fixedTime = offsetTime.DisplacedRealTime(tz=' Australia/Brisbane')

    def setup():
        fixedTime.__enter__()
        return fixedTime

    def teardown(fixedTime):
        # this seems rather odd?
        fixedTime.__exit__(None, None, None)

... но это немного неприглядно. В соответствующем вопросе jsbueno указывает: Проблема в том, что в вашем коде нет возможности вызывать метод __exit__ объекта. правильно, если возникает исключение.

Его ответ использует подход метакласса. Но это не так полезно для pytest, где тесты часто представляют собой просто функции, а не классы. Итак, каким будет pytest-y способ решить эту проблему? Что-то, связанное с перехватчиками запуска?


person pfctdayelise    schedule 04.04.2013    source источник


Ответы (2)


Начиная с версии 2.4, py.test поддерживает фикстуру стиля yield. Мы могли бы использовать контекст with внутри него напрямую.

@pytest.yield_fixture
def passwd():
    with open("/etc/passwd") as f:
        yield f.readlines()

Начиная с версии 3.0, использование py.test @pytest.yield_fixture устарело. Мы могли бы использовать @pytest.fixture напрямую в качестве менеджера контекста.

@pytest.fixture
def passwd():
    with open("/etc/passwd") as f:
        yield f.readlines()
person Jiangge Zhang    schedule 08.04.2015
comment
Я думаю, что приспособления в стиле доходности устарели? - person Neil G; 25.08.2016
comment
@NeilG Спасибо за ваш комментарий. Я обновил ответ для изменений pytest 3.0. - person Jiangge Zhang; 26.08.2016

Боюсь, в настоящее время нет элегантного способа использования контекстных менеджеров в фикстурах. Однако финализаторы будут работать, если тест не пройден:

import contextlib, pytest

@contextlib.contextmanager
def manager():
    print 'manager enter'
    yield 42
    print 'manager exit'

@pytest.fixture
def fix(request):
    m = manager()
    request.addfinalizer(lambda: m.__exit__(None, None, None))
    return m.__enter__()

def test_foo(fix):
    print fix
    raise Exception('oops')

Если вы запустите это с pytest -s, вы увидите, что происходит вызов __exit__().

person flub    schedule 04.04.2013
comment
Меня беспокоит не то, что __exit__ может не вызываться, а то, что он не будет вызываться с отсылкой правильные значения. __exit__ обычно вызывается со значениями, относящимися к любому исключению, возбужденному в блоке with (или в этом случае это будет тело теста). - person pfctdayelise; 05.04.2013