鼻子的奇怪行为

时间:2012-06-16 08:32:37

标签: python unit-testing nose

我尝试使用鼻子测试 但是当我运行下面的测试用例时

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]

但所有测试用例都未通过

$> 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)

发生了什么事?我希望在运行test_case_1之后,将调用tearDown,因此self.b[]。因此,对于下一个测试用例test_case_2setUp运行,self.b[10, 20]

但实际上,setUp的价值为self.b is [10, 20, 30]

我不知道为什么。我认为陈述self.b = []必定存在一些问题。

我猜是否有任何相关指针? 我仍然没弄清楚,但我找到了解决这个问题的方法。只需将self.b = []更改为del self.b[:]

任何人都可以帮我找出问题所在? 非常感谢你。

3 个答案:

答案 0 :(得分:4)

据我所知,问题可能是unitests的工作方式以及类字段在python中的工作原理这是一个简单的测试:

class A:
    b = []
    def reset(self):
        self.b = []

a = A()    
a.b.append(3) # we are actually accessing the class variable here
print A.b is a.b # True
print a.b # [3] same here
a.reset() # We just hid the class variable with our own which points to []
print A.b is a.b # False as expected.
print a.b # [] we think its being clear but rather we are using our own not the class variable

b = A()
print b.b # [3] b here is using the class that a previously modified but is no longer pointing to
print b.b is A.b # True

# Also note
c = A()
d = A()
print c.b is d.b # True, since both are using the same class variable.

我认为unittest是多次创建对象,为每个测试函数创建对象,触发类变量的触发设置,测试运行,调用简单隐藏它的teardown,创建另一个对象,调用访问它的设置上一个对象修改后的类变量,因为它的拆除只是创建了一个绑定到self的新实例,隐藏了类版本。

我们总是使用self来声明__init__中的成员字段。

def __init__(self):
    self.b = []

这样每个实例都有自己的副本,但我们不能在这里执行此操作,因为我们继承unittest.TestCase这就是为什么我们有setUp

import unittest
class TestSuite(unittest.TestCase):
    def setUp(self):
        self.b = [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]

答案 1 :(得分:2)

问题是第2行中的class属性:

b = []

这是类TestSuite的属性。 Nosetests创建类TestSuite的新实例,然后调用setUp。在setUp中,您修改了课程TestSuite上的属性:

self.b.extend([10, 20])

然后,在tearDown中,在运行第一个测试后,您创建一个新列表并将其分配给新的实例属性,该属性也恰好称为{{1 }}:

b

这根本不会修改属性。进一步尝试从此实例访问self.b = [] 将返回实例属性,而不是类属性。

但这没有任何效果,因为接下来发生的事情是,nosetests会抛弃self.b的当前实例,包括实例属性中的新空列表{{您在TestSuite中设置的1}}。

然后,nosetests会创建一个全新的类b实例,以进行第二次测试。但是,课程tearDown仍然包含TestSuite 属性,其中包含TestSuite,因为它在您的第一次测试运行时已被修改。

然后运行nosetests b方法,将[10, 20, 30]setUp添加到10属性20。然后你的第二个测试运行,将TestSuite添加到b属性,你的第二个测试失败,因为它在中找到六个项目>属性:

40

TestSuite有效的原因是,就像[10,20,30,10,20,40] 一样,del self.b[:]正在修改属性,而不是创建新的实例属性。

确保你理解类和实例之间的区别,以及类属性和实例属性之间的区别,否则只要你使用Python就会遇到类似的问题。

答案 2 :(得分:0)

好吧,尝试替换:

self.b.extend([10,20])

self.b = [10,20]

并且可能抛出类变量,你不需要它,这可能是发生这种情况的原因。