我正在处理一个项目,该项目涉及连接到远程服务器,等待响应,然后根据该响应执行操作。我们捕获了几个不同的异常,并根据捕获的异常来表现不同。例如:
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__钩子,但是我不知道在哪里可以找到它,或者同时我能做什么。我对unittest.mock.patch()也比较陌生,所以很有可能我也做错了什么。
这是一个Fusion360插件--所以它使用的是Fusion360的Python3.3打包版本--据我所知,这是一个普通版本(也就是说,他们没有自己的版本),但我对此并不乐观。
发布于 2015-08-07 09:13:28
我可以用一个最小的例子来重现这个错误:
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.ConnectionError进行真正的异常。但是问题仍然存在,子句试图捕获一个模拟。
TL/DR:当您模拟完整的requests包时,except requests.exceptions.ConnectionError子句试图捕获一个模拟。因为模拟不是真正的BaseException,所以它会导致错误。
我能想象的唯一解决方案不是模拟完整的requests,而只是那些不例外的部分。我必须承认,除了这一点之外,我无法找到如何模拟所有的东西,但在您的示例中,您只需要修补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方法。
发布于 2016-03-25 20:15:41
我在试图模仿sqlite3时遇到了同样的问题(并且在寻找解决方案时发现了这篇文章)。
塞尔日所说的是正确的:
TL/DR:当您模拟完整的请求包时,除了requests.exceptions.ConnectionError子句试图捕获一个模拟。因为模拟不是真正的BaseException,所以它会导致错误。 我能想象的唯一解决方案不是模拟完整的请求,而是只模拟不属于例外的部分。我必须承认,除了这句话之外,我找不出怎么说嘲弄一切
我的解决方案是模拟整个模块,然后为异常设置模拟属性,使其与实际类中的异常相等,从而有效地“非模拟”异常。例如,在我的例子中:
@mock.patch(MyClass.sqlite3)
def test_connect_fail(self, mock_sqlite3):
mock_sqlite3.connect.side_effect = sqlite3.OperationalError()
mock_sqlite3.OperationalError = sqlite3.OperationalError
self.assertRaises(sqlite3.OperationalError, MyClass, self.db_filename)对于requests,您可以像这样单独地分配异常:
mock_requests.exceptions.ConnectionError = requests.exceptions.ConnectionError或者对所有的requests异常都这样做:
mock_requests.exceptions = requests.exceptions我不知道这是否是“正确”的方法,但到目前为止,它似乎对我没有任何问题。
发布于 2016-09-03 00:17:08
对于那些需要模拟异常而不能简单修补head的人,这里有一个简单的解决方案,可以用空异常替换目标异常:
假设我们有一个要测试的通用单元,但我们必须对其进行模拟:
# app/foo_file.py
def test_me():
try:
foo()
return "No foo error happened"
except CustomError: # <-- Mock me!
return "The foo error was caught"我们想要模拟CustomError,但是因为它是一个例外,所以如果我们试图像其他所有东西一样对它进行修补,就会遇到麻烦。通常,对patch的调用将目标替换为MagicMock,但在这里不起作用。嘲弄是巧妙的,但它们的行为不像例外。与其用模拟进行修补,不如给它一个存根异常。我们会在我们的测试文件中这样做。
# app/test_foo_file.py
from mock import patch
# A do-nothing exception we are going to replace CustomError with
class StubException(Exception):
pass
# Now apply it to our test
@patch('app.foo_file.foo')
@patch('app.foo_file.CustomError', new_callable=lambda: StubException)
def test_foo(stub_exception, mock_foo):
mock_foo.side_effect = stub_exception("Stub") # Raise our stub to be caught by CustomError
assert test_me() == "The error was caught"
# Success!那么lambda是怎么回事?new_callable参数调用我们给它的任何东西,并用该调用的返回替换目标。如果我们直接传递我们的StubException类,它将调用类的构造函数,并用异常实例来修补目标对象,而不是我们想要的类。通过使用lambda包装它,它将返回我们想要的类。
完成修补后,stub_exception对象(实际上是我们的StubException类)可以引发并捕获,就好像它是CustomError一样。整洁!
https://stackoverflow.com/questions/31713054
复制相似问题