Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

py.test - how to use a context manager in a funcarg/fixture

Closely related: In python, is there a good idiom for using context managers in setup/teardown


I have a context manager that is used in tests to fix the time/timezone. I want to have it in a pytest funcarg (or fixture, we are using pytest 2.2.3 but I can translate backwards). I could just do this:

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)

... but it's a bit icky. In the related Q jsbueno points out: The problem is that your code has no provision to call the object's __exit__ method properly if an exception occurs.

His answer uses a metaclass approach. But this is not that useful for pytest where often tests are just functions, not classes. So what would be the pytest-y way to solve this? Something involving runtest hooks?

like image 578
pfctdayelise Avatar asked Apr 04 '13 02:04

pfctdayelise


People also ask

Can a Pytest fixture be a test?

Pytest fixtures are functions that can be used to manage our apps states and dependencies. Most importantly, they can provide data for testing and a wide range of value types when explicitly called by our testing software. You can use the mock data that fixtures create across multiple tests.

How do I call a Pytest fixture?

To access the fixture function, the tests have to mention the fixture name as input parameter. Pytest while the test is getting executed, will see the fixture name as input parameter. It then executes the fixture function and the returned value is stored to the input parameter, which can be used by the test.

What is the context manager statement in Python?

Python provides an easy way to manage resources: Context Managers. The with keyword is used. When it gets evaluated it should result in an object that performs context management. Context managers can be written using classes or functions(with decorators).


2 Answers

Since 2.4, py.test has yield style fixture support. We could use a with context inside it directly.

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

Since 3.0, py.test deprecated the @pytest.yield_fixture usage. We could use @pytest.fixture as a context manager directly.

@pytest.fixture
def passwd():
    with open("/etc/passwd") as f:
        yield f.readlines()
like image 69
Jiangge Zhang Avatar answered Sep 20 '22 17:09

Jiangge Zhang


I'm afraid there is currently no elegant way of using context managers in fixtures. However the finalizers will run if the test fails:

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')

If you run this with pytest -s you will see that the __exit__() call happens.

like image 24
flub Avatar answered Sep 19 '22 17:09

flub