Reputation: 4530
I have a test setup like this:
def test1():
with Manager1() as m1, m1.get_m2() as m2:
do_test1(m1, m2)
def test2():
with Manager1() as m1, m1.get_m2() as m2:
do_test2(m1, m2)
which I run with nosetests
. Now it turns out that the context managers' __enter__()
and __exit__()
methods are expensive, so I would like to change to a setup like:
def test():
with Manager1() as m1, m1.get_m2() as m2:
do_test1(m1, m2)
do_test2(m1, m2)
but I would like nose to still report separately for each test. I looked into module level and class level setup()
and teardown()
methods, but they don't seem to provide the same guarantees for cleanup in the case of exceptions. Is there a clean way to get nose
to collaborate with context managers?
Upvotes: 1
Views: 422
Reputation: 40753
If you are worried about an exception being raised in the setup
causing the teardown
method not to be called then you could use the ExitStack
class in contextlib
. The ExitStack
can make sure Your manager classes are closed properly if the case of an exception in setup
.
from io import StringIO
from contextlib import ExitStack
managers = []
close_managers = None
def setup():
global close_managers
with ExitStack() as exit_stack:
managers[:] = [exit_stack.enter_context(StringIO()) for _ in range(2)]
# any exception raised before this line will will cause all the __exit__
# methods of the given context managers to be called. So this should
# be the last part of setup
close_managers = exit_stack.pop_all().close
def teardown():
m1, m2 = managers
assert m1.getvalue() == 'test1 m1\ntest2 m1\n'
assert m2.getvalue() == 'test1 m2\ntest2 m2\n'
close_managers()
def test1():
m1, m2 = managers
m1.write('test1 m1\n')
m2.write('test1 m2\n')
def test2():
m1, m2 = managers
m1.write('test2 m1\n')
m2.write('test2 m2\n')
Upvotes: 1
Reputation: 6567
Have a look at nose test generation. It might work if you change the code to use yield, something like:
from nose.tools import nottest
@nottest
def do_test1(m1, m2):
m1.write('do_test1 f1')
m2.write('do_test1 f2')
@nottest
def do_test2(m1, m2):
m1.write('do_test2 f1')
m2.write('do_test2 f2')
def test():
with open('f1.txt', 'wb') as m1, open('f2.txt', 'wb') as m2:
yield do_test1, m1, m2
yield do_test2, m1, m2
Running tests gets you:
$ nosetests context.py -v
context.test(<open file 'f1.txt', mode 'wb' at 0x06A5BF40>, <open file 'f2.txt', mode 'wb' at 0x06A5BF98>) ... ok
context.test(<open file 'f1.txt', mode 'wb' at 0x06A5BF40>, <open file 'f2.txt', mode 'wb' at 0x06A5BF98>) ... ok
----------------------------------------------------------------------
Ran 2 tests in 0.004s
OK
Upvotes: 2