Python在覆盖失败时对异常进行预测试

时间:2010-04-15 18:00:30

标签: python unit-testing exception-handling runtime-error code-coverage

我最近遇到了一个简单但令人讨厌的错误。 我有一个清单,我想找到其中最小的成员。我使用了Python的内置min()。 一切都很好,直到在一些奇怪的情况下列表是空的(由于我无法预料的奇怪的用户输入)。我的应用程序崩溃了一个ValueError(BTW - 未在官方文档中记录)。

我有非常广泛的单元测试,我会定期检查覆盖范围以避免这样的意外。我也使用Pylint(一切都集成在PyDev中),我从不忽略警告,但是在用户用之前我没有抓住这个bug。

我可以在方法中更改以避免出现这类运行时错误吗?  (在Java / C#编译时会被捕获。)。

我正在寻找的东西不仅仅是用我的代码包装我的代码 - 除了。我还可以做些什么?有多少其他构建的Python函数隐藏了这样的令人讨厌的惊喜???

4 个答案:

答案 0 :(得分:7)

这里的问题是,外部输入格式错误导致程序崩溃。解决方案是在代码边界对各种可能的输入方案进行详尽的单元测试。你说你的单元测试是“广泛的”,但你显然没有测试过这种可能性。代码覆盖率是一个有用的工具,但重要的是要记住,覆盖代码与彻底测试代码相同。彻底的测试是覆盖使用场景和代码行的组合。

我使用的方法是信任内部呼叫者,但绝不信任外部呼叫者或输入。所以我明确对接收外部输入的第一个函数之外的任何代码中的空列表情况进行单元测试。但是输入功能应该详尽覆盖。

在这种情况下,我认为库的异常是合理的行为 - 请求空列表的min是没有意义的。例如,库可能无法合法地为您设置诸如0的值,因为您可能正在处理负数。

我认为空列表永远不会到达要求min的代码 - 它应该在输入时被识别,并在那里引发异常,或者如果它对你有用,则将其设置为0,或其他任何对你有用的东西。

答案 1 :(得分:5)

即使在Java / C#中,还有一类异常,RuntimeError未经检查,编译器也不会检测到(这就是为什么它们被称为RuntimeError而不是CompileError)。

在python中,某些异常(例如KeyboardInterrupt)特别多毛,因为它几乎可以在程序中的任意点处引发。

  

我正在寻找的东西不仅仅是用我的代码包装我的代码 - 除了。

除此之外的任何事情。让异常进入用户并暂停程序而不是让错误无声地传递(Python的禅宗)会好得多。

与Java不同,Python不需要捕获所有异常,因为要求捕获所有异常会使程序员很容易忽略异常(通过编写空白异常处理程序)。

放松一下,让错误停止;让用户向您报告,以便您可以修复它。另一种选择是你进入调试器四十二小时,因为客户的数据由于空白的强制异常处理程序而在任何地方都被破坏。

所以,你应该改变你的方法论,认为异常是坏的;他们不漂亮,但他们比其他选择更好。

答案 2 :(得分:1)

您可以使用随机测试:

#!/usr/bin/env python
import random
from peckcheck import TestCase, an_int, main

def a_seq(generator):
    return lambda size: [generator(size) 
                         for _ in xrange(random.randrange(size))] 

class TestMin(TestCase):
    def testInputNoThrow(self, x=a_seq(an_int)):
        min(x)

if __name__=="__main__":
    main()

要安装peckcheck,请输入:

$ pip install http://github.com/downloads/zed/peckcheck/peckcheck-0.1.v2.6.tar.gz

或者只是grub peckcheck.py

答案 3 :(得分:0)

我不知道你问题的直接答案;如果pylint警告这种可能性,我也会喜欢它。鉴于空列表在各种情况下都会出现问题,我的一般做法是在使用它们之前检查真相列表;例如:

val = min(vals) if vals else 0

在许多情况下,这是“免费”的,因为您经常需要检查None。它还可以为特殊情况的空列表提供性能支付,以避免(即启动新线程,进程或数据库事务以处理零项。)