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>
.