首页 > 解决方案 > unittest 从迭代器收到 None 而不是引发的错误

问题描述

我仍在学习 unittest,因此无法判断下面 test_iterators.py 中的测试用例中是否缺少某些内容。有人可以帮我理解为什么在单元测试中未能提出 ValueError 吗?以下是脚本:

迭代器.py

"""
Simple class to count from zero to N
"""
class count_to(object):
    def __init__(self, nber):
        self.nber = nber

    def __iter__(self):
        return count_to_iter(self.nber)


class count_to_iter(object):
    def __init__(self, nber):
        self.stopat = nber
        self.current_nber = 0

    def __next__(self):
        if self.stopat < 0:
            raise ValueError
        elif self.current_nber > self.stopat:
            raise StopIteration

        self.current_nber += 1

        return self.current_nber - 1


if __name__ == '__main__':
    for x in count_to(-1):
        print(x)

测试/test_iterators.py

import unittest
import iterators

class TestBaseIterators(unittest.TestCase):
    def setUp(self):
        pass

    # Can't get the negative test right yet. It returns None instead of raising a ValueError
    # Calling iterators.py directly and execute main successfully raised a ValueError however
    def test_negative(self):
        with self.assertRaises(ValueError): iterators.count_to(-1)


if __name__ == '__main__':
    unittest.main()

我之前使用过类似的方法来测试引发的错误并且它有效。但是,对于这个特定的测试用例,这是我从测试中得到的。

test_negative (test_iterators.TestBaseIterators) ... FAIL
NoneType: None

======================================================================
FAIL: test_negative (test_iterators.TestBaseIterators)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/kerwei/Git/Concepts/tests/test_iterators.py", line 19, in test_negative
    with self.assertRaises(ValueError): iterators.count_to(-1)
AssertionError: ValueError not raised

----------------------------------------------------------------------
Ran 1 test in 0.004s

FAILED (failures=1)

如果我直接从 调用迭代器__main__,我可以成功接收ValueError

(py36) Kers-MacBook-Air:Concepts kerwei$ python iterators.py
Traceback (most recent call last):
  File "iterators.py", line 29, in <module>
    for x in count_to(-1):
  File "iterators.py", line 19, in __next__
    raise ValueError
ValueError

标签: pythonpython-3.xunit-testing

解决方案


count_to(-1)创建一个新count_to实例,它不会对其进行迭代,但是您将测试放在self.stop_atvalue 上并ValueError在方法中引发 ,因此在迭代实例count_to_iter.__next__之前显然不会得到 ValueError 。count_to

天真的解决方法是强制迭代,即:

def test_negative(self):
    with self.assertRaises(ValueError): 
        # passing the iterable to `list` will force iteration
        list(iterators.count_to(-1))

但根本问题实际上更多是设计问题:此时引发 ValueError 远非最佳,因为它只会在实际使用可迭代时发生,因此您必须检查调用堆栈,直到找到count_to传递的位置一个错误的值。一个更好的解决方案是检查该值并最终直接在count_to实例化点处引发,因此它总是立即中断(而不是“最终,当您尝试在代码的某些可能远程部分使用迭代器时):

class count_to(object):
    def __init__(self, nber):
        if nber < 0:
            raise ValueError("count_to argument must be a positive integer")
        self.nber = nber

    def __iter__(self):
        return count_to_iter(self.nber)

然后您当前的测试代码将按预期工作。


推荐阅读