py.test - как да използвате контекстен мениджър във funcarg/fixture

Тясно свързано: В Python има ли добър идиом за използване на контекстни мениджъри в setup/teardown


Имам контекстен мениджър, който се използва в тестове за коригиране на часа/часовата зона. Искам да го имам в pytest funcarg (или fixture, ние използваме 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)

... но е малко неприятно. В свързаното Q jsbueno посочва: Проблемът е, че вашият код няма разпоредба за извикване на __exit__ метода на обекта правилно, ако възникне изключение.

Отговорът му използва подход на метаклас. Но това не е толкова полезно за pytest, където често тестовете са просто функции, а не класове. И така, какъв би бил най-добрият начин за решаване на това? Нещо, включващо кукички за тестване на изпълнение?


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