Reputation: 2729
I have a class full of tests that begin with a with
clause to set up a complicated set of environments and resources. Each of the with lines is long and repetitive.
I'd like to convert these to a single implementation as setUp()
and teardDown()
Essentially I want something like:
def setUp(self):
self.unit = # some long initialisation
def tearDown(self):
self.unit.__exit__()
For example: imagine this is simplified to use open()
class SomeTest(unittest.TestCase):
def test_1(self):
with open('filename', 'r') as f:
# do testing with f
...
def test_100(self):
with open('filename', 'r') as f:
# do testing with f
I'd convert it to:
class SomeTest(unittest.TestCase):
def setUp(self):
self.f = open("filename', 'r')
def tearDown(self):
self.f.close()
def test_1(self):
# do testing with self.f
...
def test_100(self):
# do testing with self.f
Now this works because file has a common, easily reproduced __exit__
equivalent namely close()
.
In our case the __exit__
handler is fairly complex, type dependent, and stateful - and (except for test-purposes) we don't want to expose a close()
like API (because that adds stateful complexity) and would allow clients not to use the with
clause as designed.
Is there some way to invoke __exit__
appropriately? Or integrate the with as
clause around each test case?
Upvotes: 0
Views: 63
Reputation: 2729
In the end decorators is probably more elegant that setUp
, tearDown
(with lots of arguments skipped)
def with_single_unit(fn):
@functools.wraps(fn)
def with_single_unit_(self):
with SingleUnitStage(lotsa_args_skipped).select(lotsa_args_skipped) as unit:
self.unit = unit
fn(self)
return with_single_unit_
class SomeTest(unittest.TestCase):
@with_single_unit
def test_a(self):
# use self.unit
@with_single_unit
def test_b(self):
# use self.unit
@with_single_unit
def test_c(self):
# use self.unit
Upvotes: 1
Reputation: 2243
As I commented, I would think that calling self.cm.__exit__(None, None, None)
in the tearDown
method would work just fine.
def tearDown(self):
self.cm.__exit__(None, None, None)
However, I got to thinking about another pattern you could try:
from contextlib import contextmanager
import unittest
class SomeTest(unittest.TestCase):
@contextmanager
def harness(self):
with open('file.txt', 'r') as f1, open('file2.txt', 'w') as f2:
try:
yield f1, f2
finally:
pass
def test_one(self):
with self.harness() as (f1, f2):
pass
Upvotes: 1