我正在一个项目中,该项目涉及连接到远程服务器,等待响应,然后根据该响应执行操作。我们捕获了两个不同的异常,并且根据捕获的异常而表现不同。例如:
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在函数的子句中进行测试。但是当我运行单元测试时,我得到
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,但得到的错误大致相同。
BaseException
我已经读过,所以我认为它在__del__某处一定是一个不好的选择,但我不确定在哪里寻找它或什至可以做些什么时间。我还比较陌生,unittest.mock.patch()所以很有可能在那做错了。
__del__
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子句就无法捕获任何内容,并且我从问题中得到与示例相同的错误:
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.ConnectionErrorin强制使用真正的异常时,我正在提出一个模拟。但是问题仍然在于 该except子句试图捕获一个模拟。
TypeError
'requests.exceptions.ConnectionError': requests.exceptions.ConnectionError
except
TL / DR:在模拟完整requests程序包时,该except requests.exceptions.ConnectionError子句尝试捕获模拟。由于模拟不是真正的a BaseException,因此会导致错误。
requests
except requests.exceptions.ConnectionError
我能想象的唯一解决方案不是模拟全部,requests而是仅模拟不是异常的部分。我必须承认, 除此以外, 我无法找到如何 模拟所有内容的 模拟方法,但在您的示例中,您只需要打补丁即可requests.head。所以我认为这应该工作:
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方法,但有副作用。
head