Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unittest teardown() del all attributes

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()
like image 352
Mike Vella Avatar asked Nov 13 '14 17:11

Mike Vella


People also ask

What is tearDown ()?

tearDown()Provides an opportunity to perform cleanup after each test method in a test case ends.

What is the use of setup () and tearDown ()?

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.

What does Unittest main () do?

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.

What is test tearDown?

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.


1 Answers

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

like image 108
Kara Avatar answered Nov 11 '22 07:11

Kara