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?
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.
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.
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).
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()
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With