我使用unittest
在Python中对单元进行单元测试。据我了解,unittest
在每次测试之前调用setUp
函数,以便单元测试对象的状态相同,执行测试的顺序无关紧要。
现在我正在测试这门课程......
#! 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
进行以下单元测试......
#! 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)
test_translate的测试失败。
我可以做两件事来让测试成功:
(1)取消注释setUp函数中的第二行。或者,
(2)更改测试的名称,使translate
首先出现。我注意到unittest
按字母顺序执行测试。将translate
更改为atranslate
以使其首先执行会使所有测试成功。
对于(1),我无法想象这会如何影响测试,因为在setUp
的第一行,我们为self.st创建了一个新对象。至于(2),我的投诉是相似的,嘿,setUp
我将一个新对象分配给self.st
,所以无论我对self.st
test_set
做什么都不应该影响test_translate
的结果。
那么,我在这里错过了什么?
答案 0 :(得分:10)
如果不研究解决方案的细节,你应该阅读Fredrik Lundh的Default Parameter Values in Python。
它可能会将您的空列表作为默认参数解释您的问题。原因是该列表仅在第一次为空,除非您稍后明确地将其清空。初始为空的默认列表是在没有传递显式参数时重用的列表类型的单个实例。
最好阅读上面的文章来解决你对默认参数的看法。原因是合乎逻辑的,但可能是意料之外的。
通常建议的修复方法是使用None
作为__init__
的默认值,如果未传递参数,则在主体内设置空列表,如下所示:
class SpamTest(object):
def __init__(self, numlist=None):
if numlist is None:
numlist = [] # this is the new instance -- the empty list
self.__numlist = numlist
答案 1 :(得分:4)
这是因为当使用像列表这样的Mutable对象时,默认参数在Python中的行为方式:Default Parameter Values in Python。
在行中:
def __init__(self, numlist = []):
numlist的默认参数仅评估一次,因此您只有一个列表实例,该实例在SpamTest
类的所有实例中共享。
因此,即使为每个测试调用了测试setUp
,它也永远不会创建一个新的空列表,并且对该列表实例起作用的测试最终会踩到彼此的脚趾。
修复是使用像None
之类的非可变对象来代替这样的东西:
def __init__(self, numlist = None):
if numlist is None:
numlist = []
self.__numlist = numlist
设置属性时它起作用的原因是你在那里提供了一个全新的空列表,替换了在构造函数中创建的列表。