Reputation: 21747
I have a test-case with both a setUp
and tearDown
method:
TESTCONF = SafeConfigParser(...)
ENGINE = create_engine(TESTCONF.get('database', 'dsn'))
class TestBase(TestCase):
def setUp(self):
self.config = TESTCONF
self.connection = ENGINE.connect()
self.trans = self.connection.begin()
self.session = Session(bind=self.connection)
def tearDown(self):
print "post teardown 0", ENGINE.pool.status()
self.trans.rollback()
print "post teardown 1", ENGINE.pool.status()
self.session.close()
print "post teardown 2", ENGINE.pool.status()
self.connection.close()
print "post teardown 3", ENGINE.pool.status(), "\n"
All DB related test-cases inherit from this class. It seems as if tearDown
is not always called. I am having trouble to localize the error. At one point, the test-runner hangs on
self.connection = ENGINE.connect()
I assume that the close
method was not always called to release a connection from the pool.
Any ideas what to look for?
Update: I how added some print statements (also added them in the example code above), and my initial though was correct. Some connections are not properly closed and are not handed back to the pool. This happens for all "Errors" (not "Failures") in the tests. The following block shows the output I get using the above tearDown
method (cut off prematurely for brevity). As you can see, the lines with errors (those starting with Epre
instead of .pre
) do not call any tearDown
lines. Not even the post tearDown 0
message shows up!
I have now traced back the error to using self.assertRaisesRegEx
instead of self.assertRaisesRegExp
, so the exception is raised inside the unit-test, not inside the tested code!
pre setup Pool size: 5 Connections in pool: 0 Current Overflow: -5 Current Checked out connections: 0
post connect Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 0 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 1 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 2 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 3 Pool size: 5 Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
.pre setup Pool size: 5 Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
post connect Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 0 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 1 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 2 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 3 Pool size: 5 Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
.pre setup Pool size: 5 Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
post connect Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 0 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 1 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 2 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 3 Pool size: 5 Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
.pre setup Pool size: 5 Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
post connect Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
'TestCommonBase' object has no attribute 'assertRaisesRegex'
post teardown 0 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 1 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 2 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 3 Pool size: 5 Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
.pre setup Pool size: 5 Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
post connect Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 0 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 1 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 2 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 3 Pool size: 5 Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
.pre setup Pool size: 5 Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
post connect Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 0 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 1 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 2 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 3 Pool size: 5 Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
.pre setup Pool size: 5 Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
post connect Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 0 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 1 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 2 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 3 Pool size: 5 Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
.pre setup Pool size: 5 Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
post connect Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
'TestCommonBase' object has no attribute 'assertRaisesRegex'
post teardown 0 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 1 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 2 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 3 Pool size: 5 Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
.pre setup Pool size: 5 Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
post connect Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 0 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 1 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 2 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 3 Pool size: 5 Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
.pre setup Pool size: 5 Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
post connect Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 0 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 1 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 2 Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 3 Pool size: 5 Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
.SSpre setup Pool size: 5 Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
post connect Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
ESpre setup Pool size: 5 Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post connect Pool size: 5 Connections in pool: 0 Current Overflow: -3 Current Checked out connections: 2
Epre setup Pool size: 5 Connections in pool: 0 Current Overflow: -3 Current Checked out connections: 2
post connect Pool size: 5 Connections in pool: 0 Current Overflow: -2 Current Checked out connections: 3
Epre setup Pool size: 5 Connections in pool: 0 Current Overflow: -2 Current Checked out connections: 3
post connect Pool size: 5 Connections in pool: 0 Current Overflow: -1 Current Checked out connections: 4
Epre setup Pool size: 5 Connections in pool: 0 Current Overflow: -1 Current Checked out connections: 4
post connect Pool size: 5 Connections in pool: 0 Current Overflow: 0 Current Checked out connections: 5
Epre setup Pool size: 5 Connections in pool: 0 Current Overflow: 0 Current Checked out connections: 5
post connect Pool size: 5 Connections in pool: 0 Current Overflow: 1 Current Checked out connections: 6
Epre setup Pool size: 5 Connections in pool: 0 Current Overflow: 1 Current Checked out connections: 6
post connect Pool size: 5 Connections in pool: 0 Current Overflow: 2 Current Checked out connections: 7
Epre setup Pool size: 5 Connections in pool: 0 Current Overflow: 2 Current Checked out connections: 7
post connect Pool size: 5 Connections in pool: 0 Current Overflow: 3 Current Checked out connections: 8
Epre setup Pool size: 5 Connections in pool: 0 Current Overflow: 3 Current Checked out connections: 8
post connect Pool size: 5 Connections in pool: 0 Current Overflow: 4 Current Checked out connections: 9
Epre setup Pool size: 5 Connections in pool: 0 Current Overflow: 4 Current Checked out connections: 9
post connect Pool size: 5 Connections in pool: 0 Current Overflow: 5 Current Checked out connections: 10
Epre setup Pool size: 5 Connections in pool: 0 Current Overflow: 5 Current Checked out connections: 10
post connect Pool size: 5 Connections in pool: 0 Current Overflow: 6 Current Checked out connections: 11
Epre setup Pool size: 5 Connections in pool: 0 Current Overflow: 6 Current Checked out connections: 11
post connect Pool size: 5 Connections in pool: 0 Current Overflow: 7 Current Checked out connections: 12
Epre setup Pool size: 5 Connections in pool: 0 Current Overflow: 7 Current Checked out connections: 12
post connect Pool size: 5 Connections in pool: 0 Current Overflow: 8 Current Checked out connections: 13
Epre setup Pool size: 5 Connections in pool: 0 Current Overflow: 8 Current Checked out connections: 13
post connect Pool size: 5 Connections in pool: 0 Current Overflow: 9 Current Checked out connections: 14
Epre setup Pool size: 5 Connections in pool: 0 Current Overflow: 9 Current Checked out connections: 14
post connect Pool size: 5 Connections in pool: 0 Current Overflow: 10 Current Checked out connections: 15
post teardown 0 Pool size: 5 Connections in pool: 0 Current Overflow: 10 Current Checked out connections: 15
post teardown 1 Pool size: 5 Connections in pool: 0 Current Overflow: 10 Current Checked out connections: 15
post teardown 2 Pool size: 5 Connections in pool: 0 Current Overflow: 10 Current Checked out connections: 15
post teardown 3 Pool size: 5 Connections in pool: 1 Current Overflow: 10 Current Checked out connections: 14
.pre setup Pool size: 5 Connections in pool: 1 Current Overflow: 10 Current Checked out connections: 14
post connect Pool size: 5 Connections in pool: 0 Current Overflow: 10 Current Checked out connections: 15
Epost teardown 0 Pool size: 5 Connections in pool: 0 Current Overflow: 10 Current Checked out connections: 15
post teardown 1 Pool size: 5 Connections in pool: 0 Current Overflow: 10 Current Checked out connections: 15
post teardown 2 Pool size: 5 Connections in pool: 0 Current Overflow: 10 Current Checked out connections: 15
post teardown 3 Pool size: 5 Connections in pool: 1 Current Overflow: 10 Current Checked out connections: 14
For the sake of this question, I wrote a very small reproducible example. Unfortunately that one did not exhibit this behaviour:
class MyTest(TestCase):
"""
Example test case meant to demonstrate that ``tearDown`` is not called.
It turns out, in this case, ``tearDown`` *is* called as expected!
"""
def setUp(self):
print "setup"
def tearDown(self):
print "tearDown"
def test_failing(self):
print int('yes')
if __name__ == '__main__':
main()
So what makes this example different from my real-world code? Why is tearDown
called in the simple example, but not in my production code?
I will continue to investigate...
Upvotes: 5
Views: 2861
Reputation: 1391
If you suspect that tearDown
is not called, then I can propose you to use context manager for resource allocation in your tests. The with
statement guarantees that if the __enter__()
method returns without an error, then __exit__()
will always be called.
Here is your example modified to use context manager for connection allocation:
TESTCONF = SafeConfigParser(...)
ENGINE = create_engine(TESTCONF.get('database', 'dsn'))
class DBConnection(object):
def __init__(self, engine):
self.engine = engine
def __enter__(self):
self.connection = engine.connect()
self.trans = self.connection.begin()
self.session = Session(bind=self.connection)
# return value can be accessed using `as` directive
return self.connection, self.trans, self.session
def __exit__(self, exc_type, exc_val, traceback):
self.trans.rollback()
self.session.close()
self.connection.close()
class TestBase(unittest.TestCase):
def setUp(self):
self.config = TESTCONF
def run(self, result=None):
with DBConnection(ENGINE) as db_conn:
self.connection, self.trans, self.session = db_conn
super(MyTest, self).run(result)
Also you can use contextlib if DBConnection class seems too bulky:
from contextlib import contextmanager
@contextmanager
def DBConnection(engine):
connection = engine.connect()
trans = connection.begin()
session = Session(bind=connection)
yield connection, trans, session
trans.close()
session.close()
connection.close()
Upvotes: 1