In this article, I shared my experience with a weird bug in nosetests. Let ’s
see what is it, and what we could do to fix/avoid it in future.
With unittest, two functions setUp and tearDown will call before and after
every test case function. With code bellow, I expected test cases will be passed:
import unittest
class TestSuite(unittest.TestCase):
b = []
def setUp(self):
self.b.extend([10, 20])
def tearDown(self):
self.b = []
def test_case_1(self):
self.b.append(30)
assert len(self.b) == 3
assert self.b == [10, 20, 30]
def test_case_2(self):
self.b.append(40)
assert len(self.b) == 3
assert self.b == [10, 20, 40]But when I run it with nose, the result is unexpected:
$> nosetest test_module.py
.F
======================================================================
FAIL: test_case_2 (test_module2.TestSuite)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/knt/test_module2.py", line 19, in test_case_2
assert len(self.b) == 3
AssertionError
----------------------------------------------------------------------
Ran 2 tests in 0.001s
FAILED (failures=1)What ’s happend ??? I expect after run test_case_1, tearDown will be
called, so self.b is []. With next test case test_case_2, setUp
run and self.b is [10, 20]. But in fact, at setUp value of self.b is
[10, 20, 30]. I don’t know why??? I think there must be some problems with statement
self.b = []. Anything related pointer, I guess. I still didn’t figure it out, but I find a way
to work around this bug. Just change self.b = [] to del self.b[:]:
def tearDown(self):
del self.b[:]Now, lunch time !!!
Update
I post my question on stackoverflow, and the answer explains about this bug.
In tearDown function, when I self.b = [], the b variable is not
class variable. In fact, python will create a new instance variable for this
class. After that, at next test case, nose and unittest will create new instance of
TestSuite so, self.b of this instance still is a class variable. The reason
why del self.b[:] works is it does not create a new instance variable, but
still access class variable.
This bug gives me a new lesson when dealing with class variables.
The “best practice” with me is every time I want to access class variable in
method, I should use self.__class__.<variable_name>.