assertRaises仅捕获基本异常

0 投票
3 回答
1490 浏览
提问于 2025-04-15 17:41

我在使用 unittest.assertRaises 的时候遇到了一个奇怪的问题。当我执行下面的代码时,得到了以下输出:

E
======================================================================
ERROR: testAssertRaises (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\home\python_test\src\derived.py", line 29, in testAssertRaises
    self.assertRaises(MyError, self.raiser.raiseMyError)
  File "C:\Programme\Python26\lib\unittest.py", line 336, in failUnlessRaises
    callableObj(*args, **kwargs)
  File "C:\home\python_test\src\derived.py", line 15, in raiseMyError
    raise MyError("My message")
MyError: 'My message'

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)

虽然正确的异常被抛出了,但测试却失败了!如果我捕获 BaseError,测试就成功了。

看起来这似乎是一个作用域的问题,unittest 似乎无法识别 MyError 这个异常类。有人能解释一下吗?有没有什么解决办法?

我正在测试以下 Python 代码,这段代码是用来根据类名动态构建对象的。

这是基础模块 "bases.py":

class BaseClass(object):

    @staticmethod
    def get(className):
        module = __import__("derived", globals(), locals(), [className])
        theClass = getattr(module, className)
        return theClass()


class BaseError(Exception):

    def __init__(self, msg):
        self.msg = msg

    def __str__(self):
        return repr(self.msg)

这是要测试的模块 "derived.py":

import unittest

from bases import BaseError
from bases import BaseClass


class MyErrorRaiser(BaseClass):    

    def raiseMyError(self):
        raise MyError("My message")


class MyError(BaseError):
    '''
    '''


class Test(unittest.TestCase):

    def setUp(self):
        self.raiser = BaseClass.get("MyErrorRaiser")

    def testAssertRaises(self):
        self.assertRaises(MyError, self.raiser.raiseMyError)


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

3 个回答

0

这个问题可能是因为你的 BaseClass.get() 方法返回了另一个类。顺便说一下,这个方法本身就很糟糕,我很好奇你为什么要这样做。

2

正如提到的,问题在于模块__main__derived并不是同一个东西;这段内容是关于如何解决这个问题的。

不要把模块代码和脚本代码混在一起。开始把if __name__ == "__main__"这段代码当作一种小技巧来看待。(虽然在某些时候它非常方便,我也经常用它来调试等等,但把它当作小技巧可以让你在写的时候有一点心理上的提醒。)接下来要运行的脚本会很简单(并且永远不会被导入):

#!/usr/bin/env python
import derived
import sys
sys.exit(derived.main(sys.argv[1:]))

或者根据个人喜好有各种不同的写法。只要确保你添加了derived.main来实现你想要的功能。

我还见过另一种不太常见的小技巧,放在derived.py的顶部

import sys
if __name__ == "__main__":
  import derived # import the same module under its "correct" name
  sys.exit(derived.main(sys.argv[1:]))

虽然这样做会浪费时间重复解析同一段代码,但这样做是自给自足的。

1

当你直接运行 derived.py 这个文件时,它会作为 __main__ 模块被执行(因为你是直接运行它,而不是通过其他地方引入)。但是,当你之后明确地导入这个模块时,系统会创建这个模块的另一个副本,这次它的名字是 derived。所以 __main__.MyErrorderived.MyError 其实是两个不同的东西,因此这个错误没有被捕捉到。

撰写回答