Remove Python class reference between unittests

I want to run multiple unittests on a class with some class level variables. Class level variables are not reset back to pre-run values ​​as the unittest code contains a reference to the class. Apart from resetting all class level variables in the init method in the class under test, how can I get a new class for each unittest method?

class NonEmptyClassTest(unittest.TestCase):

    def test_makeName(self):
        nec = NonEmptyClass()
        nec.addName("Fred")
        nec.printAllData()
        self.assertEquals(1 , len(nec.dummy_data))

    def test_makeName_1(self):
        nec = NonEmptyClass()
        nec.addName("Fred")
        nec.printAllData()
        self.assertEquals(1 , len(nec.dummy_data))


class NonEmptyClass(object):
    dummy_data = {}

    def printAllData(self):
        for k,v in self.dummy_data.items():
            print k, v

    def addName(self, name):
        if not name in self.dummy_data:
            self.dummy_data[name] = name+"_value"
        else:
            name = name + ".1"
            self.dummy_data[name] = name+"_value"

      

+3


source to share


4 answers


This is the module in which this class is defined, which contains the link. And the module itself is stored in sys.modules

to prevent the top-level module of the module from being re-run every time you use it in another module using an instruction import

.

Make sure your unit test is defined in a separate module and moves the import of the class into a helper function where you ensure the module is removed first before importing. This ensures that the module is re-created every time:



import sys

class NonEmptyClassTest(unittest.TestCase):
    def _makeOne(self, clear=False):
        if clear:
            try:
                del sys.modules['module_name']
            except KeyError:
                pass
        from module_name import NonEmptyClass
        return NonEmptyClass

    def test_makeName(self):
        nec = self._makeOne(clear=True)
        nec.addName("Fred")
        nec.printAllData()
        self.assertEquals(1 , len(nec.dummy_data))

    def test_makeName_1(self):
        nec = self._makeOne(clear=True)
        nec.addName("Fred")
        nec.printAllData()
        self.assertEquals(1 , len(nec.dummy_data))

      

The keyword argument clear

allows you to create multiple instances without clearing the module to check if the data is actually being shared between instances.

+2


source


You can use setUp()

to clear the dictionary and make all test cases independent. These are unit tests, and it's nice that they are independent of each other and the order of execution.

class NonEmptyClassTest(unittest.TestCase):
    def setUp(self):
        NonEmptyClass.dummy_data = {}

    def test_makeName(self):
        nec = NonEmptyClass()
        nec.addName("Fred")
        nec.printAllData()
        self.assertEquals(1 , len(nec.dummy_data))

    def test_makeName_1(self):
        nec = NonEmptyClass()
        nec.addName("Fred")
        nec.printAllData()
        self.assertEquals(1 , len(nec.dummy_data))

      

[EDIT]



From the comment. IMHO will explicitly write what a class variable is and how to reset to make the test clearer and give you some more useful breakpoints.

Of course, if you add additional class variables you MUST update the test case setUp()

, but I think that's a plus. Tests should be clear and should not hide this behavior.

+4


source


You should refrain from using mutable objects in class variables. Please refer to this tutorial: https://docs.python.org/2/tutorial/classes.html#class-and-instance-variables .

As @markcial mentioned about his original unedited answer, you need to make < dummy_data

an instance variable instead of a class variable .

Trying to set up your tests to pass instead by doing some setUp hacks and re-initializing the variables is a sign that the underlying implementation of the class you are actually testing has an implementation flaw.

So, you would rather fix the class instead of fighting against its unit test. Hence, I copy and paste @markcial's original answer here, which only captures the implementation of the class:

class NonEmptyClass(object):

    def __init__(self):
        self.dummy_data = {}

    def printAllData(self):
        for k, v in self.dummy_data.items():
            print k, v

    def addName(self, name):
        if name not in self.dummy_data:
            self.dummy_data[name] = name + "_value"
        else:
            self.dummy_data[name] = name + ".1_value"

      

+1


source


if you want your class to have the values ​​defined in the instantiaton class add a method __init__

to the class:

class NonEmptyClass(object):

    def __init__(self):
        self.dummy_data = {}

    def printAllData(self):
        for k,v in self.dummy_data.items():
            print k, v

    def addName(self, name):
        if not name in self.dummy_data:
            self.dummy_data[name] = name+"_value"
        else:
            name = name + ".1"
            self.dummy_data[name] = name+"_value"

      

EDIT

If its only testing uses a setting to override a variable from a class object:

class NonEmptyClassTest(unittest.TestCase):

    nec = None

    def setUp(self):
        NonEmptyClass.dummy_data = {}
        self.nec = NonEmptyClass()

    def test_makeName(self):
        self.nec.addName("Fred")
        self.nec.printAllData()
        self.assertEquals(1 , len(self.nec.dummy_data))

    def test_makeName_1(self):
        self.nec.addName("Fred")
        self.nec.printAllData()
        self.assertEquals(1 , len(self.nec.dummy_data))

      

-1


source







All Articles