我正在处理一个项目,该项目涉及连接到远程服务器、等待响应,然后根据该响应执行操作。我们捕获了几个不同的异常,并且根据捕获的异常而表现出不同的行为。例如:
def myMethod(address, timeout=20):
try:
response = requests.head(address, timeout=timeout)
except requests.exceptions.Timeout:
# do something special
except requests.exceptions.ConnectionError:
# do something special
except requests.exceptions.HTTPError:
# do something special
else:
if response.status_code != requests.codes.ok:
# do something special
return successfulConnection.SUCCESS
为了测试这一点,我们编写了如下测试
class TestMyMethod(unittest.TestCase):
def test_good_connection(self):
config = {
'head.return_value': type('MockResponse', (), {'status_code': requests.codes.ok}),
'codes.ok': requests.codes.ok
}
with mock.patch('path.to.my.package.requests', **config):
self.assertEqual(
mypackage.myMethod('some_address',
mypackage.successfulConnection.SUCCESS
)
def test_bad_connection(self):
config = {
'head.side_effect': requests.exceptions.ConnectionError,
'requests.exceptions.ConnectionError': requests.exceptions.ConnectionError
}
with mock.patch('path.to.my.package.requests', **config):
self.assertEqual(
mypackage.myMethod('some_address',
mypackage.successfulConnection.FAILURE
)
如果我直接运行该函数,一切都会按预期进行。我什至通过将 raise requests.exceptions.ConnectionError
添加到函数的 try
子句来进行测试。但是当我运行单元测试时,我得到了
ERROR: test_bad_connection (test.test_file.TestMyMethod)
----------------------------------------------------------------
Traceback (most recent call last):
File "path/to/sourcefile", line ###, in myMethod
respone = requests.head(address, timeout=timeout)
File "path/to/unittest/mock", line 846, in __call__
return _mock_self.mock_call(*args, **kwargs)
File "path/to/unittest/mock", line 901, in _mock_call
raise effect
my.package.requests.exceptions.ConnectionError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "Path/to/my/test", line ##, in test_bad_connection
mypackage.myMethod('some_address',
File "Path/to/package", line ##, in myMethod
except requests.exceptions.ConnectionError:
TypeError: catching classes that do not inherit from BaseException is not allowed
我试图将我正在修补的异常更改为 BaseException
,但我得到了或多或少相同的错误。
我已阅读 https://stackoverflow.com/a/18163759/3076272已经,所以我认为它一定是某个地方的错误 __del__
钩子(Hook),但我不确定在哪里寻找它,或者我什至可以在此期间做什么。我对 unittest.mock.patch()
也比较陌生,所以我很可能在那里也做错了什么。
这是一个 Fusion360 插件,因此它使用的是 Fusion 360 的 Python 3.3 打包版本——据我所知,它是一个普通版本(即它们不会自己推出),但我对此并不肯定。
最佳答案
我可以用一个最小的例子重现错误:
foo.py:
class MyError(Exception):
pass
class A:
def inner(self):
err = MyError("FOO")
print(type(err))
raise err
def outer(self):
try:
self.inner()
except MyError as err:
print ("catched ", err)
return "OK"
无需模拟即可测试:
class FooTest(unittest.TestCase):
def test_inner(self):
a = foo.A()
self.assertRaises(foo.MyError, a.inner)
def test_outer(self):
a = foo.A()
self.assertEquals("OK", a.outer())
好的,一切都很好,两个测试都通过了
问题来自于模拟。一旦模拟 MyError 类, expect
子句就无法捕获任何内容,并且我得到与问题示例相同的错误:
class FooTest(unittest.TestCase):
def test_inner(self):
a = foo.A()
self.assertRaises(foo.MyError, a.inner)
def test_outer(self):
with unittest.mock.patch('foo.MyError'):
a = exc2.A()
self.assertEquals("OK", a.outer())
马上给:
ERROR: test_outer (__main__.FooTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "...\foo.py", line 11, in outer
self.inner()
File "...\foo.py", line 8, in inner
raise err
TypeError: exceptions must derive from BaseException
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<pyshell#78>", line 8, in test_outer
File "...\foo.py", line 12, in outer
except MyError as err:
TypeError: catching classes that do not inherit from BaseException is not allowed
在这里我得到了你没有的第一个 TypeError
,因为当你用 'requests.exceptions.ConnectionError': requests.exceptions 强制一个真正的异常时,我正在引发一个模拟。配置中的连接错误
。但问题仍然存在,except
子句试图捕捉模拟。
TL/DR:当你模拟完整的 requests
包时,except requests.exceptions.ConnectionError
子句试图捕捉一个模拟。由于 mock 不是真正的 BaseException
,因此会导致错误。
我能想象的唯一解决方案不是模拟完整的 requests
而是只模拟不是异常的部分。我必须承认我找不到如何模拟 mock 除了 this 之外的所有内容,但在您的示例中,您只需要修补 requests.head
。所以我认为这应该可行:
def test_bad_connection(self):
with mock.patch('path.to.my.package.requests.head',
side_effect=requests.exceptions.ConnectionError):
self.assertEqual(
mypackage.myMethod('some_address',
mypackage.successfulConnection.FAILURE
)
即:仅修补 head
方法,将异常作为副作用。
关于python - 无法捕获模拟异常,因为它不继承 BaseException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31713054/