I'm unit testing classes in Python using unittest
. As I understand it, unittest
calls the setUp
function before each test so that the state of the unit test objects are the same and the order the test are executed wouldn't matter.
Now I have this class I'm testing...
#! usr/bin/python2
class SpamTest(object):
def __init__(self, numlist = []):
self.__numlist = numlist
@property
def numlist(self):
return self.__numlist
@numlist.setter
def numlist(self, numlist):
self.__numlist = numlist
def add_num(self, num):
self.__numlist.append(num)
def incr(self, delta):
self.numlist = map(lambda x: x + 1, self.numlist)
def __eq__(self, st2):
i = 0
limit = len(self.numlist)
if limit != len(st2.numlist):
return False
while i < limit:
if self.numlist[i] != st2.numlist[i]:
return False
i += 1
return True
with the following unit tests...
#! usr/bin/python2
from test import SpamTest
import unittest
class Spammer(unittest.TestCase):
def setUp(self):
self.st = SpamTest()
#self.st.numlist = [] <--TAKE NOTE OF ME!
self.st.add_num(1)
self.st.add_num(2)
self.st.add_num(3)
self.st.add_num(4)
def test_translate(self):
eggs = SpamTest([2, 3, 4, 5])
self.st.incr(1)
self.assertTrue(self.st.__eq__(eggs))
def test_set(self):
nl = [1, 4, 1, 5, 9]
self.st.numlist = nl
self.assertEqual(self.st.numlist, nl)
if __name__ == "__main__":
tests = unittest.TestLoader().loadTestsFromTestCase(Spammer)
unittest.TextTestRunner(verbosity = 2).run(tests)
This test fails for test_translate.
I can do two things to make the tests succeed:
(1) Uncomment the second line in the setUp function. Or,
(2) Change the names of the tests such that translate
occurs first. I noticed that unittest
executes tests in alphabetical order. Changing translate
to, say, atranslate
so that it executes first makes all tests succeed.
For (1), I can't imagine how this affects the tests since at the very first line of setUp
, we create a new object for self.st . As for (2), my complaint is similar since, hey, on setUp
I assign a new object to self.st
so whatever I do to self.st
in test_set
shouldn't affect the outcome of test_translate
.
So, what am I missing here?
Without studying the detais of your solution, you should read the Default Parameter Values in Python by Fredrik Lundh.
It is likely that it explains your problem with your empty list as a default argument. The reason is that the list is empty only for the first time unless you make it empty explicitly later. The initialy empty default list is the single instance of the list type that is reused when no explicit argument is passed.
It is good idea to read the above article to fix your thinking about the default arguments. The reasons are logical, but may be unexpected.
The generally recommended fix is to use None
as the default value of the __init__
and set the empty list inside the body if the argument is not passed, like this:
class SpamTest(object):
def __init__(self, numlist=None):
if numlist is None:
numlist = [] # this is the new instance -- the empty list
self.__numlist = numlist