Python中的单元测试对象 - 对象未在设置中重写

时间:2012-07-08 09:15:09

标签: python unit-testing unittest2

我使用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的结果。

那么,我在这里错过了什么?

2 个答案:

答案 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

设置属性时它起作用的原因是你在那里提供了一个全新的空列表,替换了在构造函数中创建的列表。