I have unit tests with setup and teardown methods which look like this:
def setUp(self):
self.foo = "bar"
self.bar = "foo"
def tearDown(self):
del self.foo
del self.bar
Is there a cleaner way to call __del__
on every object I instantiate in setUp
? If I don't do this then connections to a MySQL database which are opened in setUp
(by instantiating those objects) seem to remain open after every test.
Ideally I would of course figure out the underlying problem (why those database connections aren't being closed when the test finishes and the test case is discarded). In the meantime though, is there a cleaner way to del
all those objects?
The database connections are created using the MySQLdb
library, there is also a destructor to close the connection:
class Foo(object):
def __init__(self,some_credentials):
self.db_connection = MySQLdb.connect(some_credentials)
def __del__(self):
self.db_connection.close()
tearDown()Provides an opportunity to perform cleanup after each test method in a test case ends.
When a setUp() method is defined, the test runner will run that method prior to each test. Likewise, if a tearDown() method is defined, the test runner will invoke that method after each test.
Internally, unittest. main() is using a few tricks to figure out the name of the module (source file) that contains the call to main() . It then imports this modules, examines it, gets a list of all classes and functions which could be tests (according the configuration) and then creates a test case for each of them.
A teardown test case will execute at the end of your test run within a test folder. Teardown test cases are used to perform post test execution actions. For example, a teardown test case can be used to delete test data generated during test execution.
The underlying problem here is that each python unit test does not discard the test instance after each test case is run. The instances are kept in memory so any object assigned to self is also kept in memory until the entire suite is complete.
You can reproduce this with the following code. The memory usage will grow with each additional test that is run. If self.large_list
is set to None
in the teardown
then the memory usage remains consistent.
import resource
import unittest
import uuid
class TestSelfGarbageCollection(unittest.TestCase):
def setUp(self):
# Create an object that will use lots of memory
self.large_list = []
for _ in range(300000):
self.large_list.append(uuid.uuid4())
def tearDown(self):
# Without the following line a copy of large_list will be kept in
# memory for each test that runs, uncomment the line to allow the
# large_list to be garbage collected.
# self.large_list = None
mb_memory = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1000
print("Memory usage: %s MB" % mb_memory)
def test_memory1(self):
pass
def test_memory2(self):
pass
def test_memory3(self):
pass
def test_memory4(self):
pass
def test_memory5(self):
pass
Run with:
py.test test_large_memory.py -s -v
The simplest solution is to explicitly cleanup any large objects assigned to self
or any objects that need cleanup (eg. database connections) in tearDown
.
References:
Python’s leaky TestCase
Issue 11798: Test cases not garbage collected after run - Python tracker
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