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"
source to share
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.
source to share
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.
source to share
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"
source to share
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))
source to share