Stefan
Stefan

Reputation: 4530

Execute multiple tests in the same context managers

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

Answers (2)

Dunes
Dunes

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

Oleksiy
Oleksiy

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

Related Questions