Python在覆盖失败时的异常预测试
我最近遇到了一个简单但很棘手的错误。我有一个列表,想要找出里面最小的数字。我用了Python自带的min()函数。一切都运行得很好,直到在某个奇怪的情况下,列表变成了空的(这是因为用户输入了一些我没法预料到的奇怪内容)。我的应用程序因此崩溃了,报了一个ValueError(顺便说一下,这个错误在官方文档里没有说明)。
我有非常全面的单元测试,并且我定期检查代码覆盖率,以避免出现这样的意外。我还使用Pylint(这些都集成在PyDev里),我从来不忽视警告,但我还是没能在用户之前发现这个错误。
我可以在我的方法上做些什么改变,以避免这种运行时错误吗?(在Java或C#中,这种错误会在编译时被捕捉到)。
我希望找到一些比用一个大大的try-except包裹我的代码更好的解决办法。我还能做些什么呢?Python里还有多少其他内置函数也藏着这样的意外呢???
4 个回答
你可以使用随机测试:
#!/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
即使在Java或C#中,有一类叫做运行时错误(RuntimeError)的异常是不会被编译器检查的(这就是它们叫运行时错误而不是编译错误的原因)。
在Python中,有些异常,比如键盘中断(KeyboardInterrupt),特别麻烦,因为它可以在程序的任何地方被触发。
我希望能找到比把我的代码包裹在一个大大的try-except里更好的解决办法。
请不要这样做。让异常直接反馈给用户并停止程序,比让错误悄悄地存在要好得多(这也是Python的哲学之一)。
与Java不同,Python并不要求捕获所有异常,因为如果强制捕获所有异常,程序员就很容易忽视这些异常(比如写一个空的异常处理器)。
放轻松,让错误停止运行;让用户向你报告,这样你才能修复它。否则,你可能会花上四十二个小时去调试,因为客户的数据因为一个空的强制异常处理器而到处被破坏。
所以,你需要改变的思维方式是,不要把异常看作坏事;虽然它们不好看,但总比其他选择要好。
这里的问题是,外部输入格式不正确导致你的程序崩溃。解决办法是对代码边界可能出现的输入情况进行全面的单元测试。你说你的单元测试很“全面”,但显然你没有测试到这种情况。代码覆盖率是一个有用的工具,但要记住,覆盖代码并不等于彻底测试代码。彻底测试是覆盖使用场景和代码行的结合。
我使用的方法是信任内部调用者,但绝不信任外部调用者或输入。因此,我在任何接收外部输入的函数之外,明确不对空列表的情况进行单元测试。但这个输入函数应该被全面测试。
在这种情况下,我认为库抛出的异常是合理的行为——请求一个空列表的min
是没有意义的。这个库不能随便给你设置一个值,比如0,因为你可能在处理负数。
我认为空列表不应该到达请求min
的那段代码——它应该在输入时就被识别出来,并在那时抛出异常,或者如果你觉得合适,可以将其设置为0,或者其他任何适合你的处理方式。