注意:此问题基于previous question I asked,但根据this answer 修改为不同。
我使用side_effect,试图在调用模拟程序时引发'URLError'
异常,但遇到一个我不理解的DID NOT RAISE
错误。
我有一个带有类方法Query
的{{1}}类,这可能引发几个异常。我不在make_request_and_get_response
的{{1}}方法内捕获'URLError'
异常,所以我应该应该会被引发并随后对其异常进行了模拟。
query.py
get_response_from_external_api
main.py
main.py
我正在使用from urllib.request import urlopen
import contextlib
import urllib
class Query:
def __init__(self, a, b):
self.a = a
self.b = b
self.query = self.make_query()
def make_query(self):
# create query request using self.a and self.b
return query
def make_request_and_get_response(self): # <--- the 'dangerous' method that can raise exceptions
with contextlib.closing(urlopen(self.query)) as response:
return response.read().decode('utf-8')
来模拟该“危险”方法(from foo.query import *
def get_response_from_external_api(query):
try:
response = query.make_request_and_get_response()
except urllib.error.HTTPError as e:
print('Got a HTTPError: ', e)
except Exception:
print('Got a generic Exception!')
# handle this exception
if __name__ == "__main__":
query = Query('input A', 'input B')
result = get_response_from_external_api(query)
return result
),并带有针对特定异常的副作用。然后,我将继续创建一个模拟的pytest
对象,以在调用make_request_and_get_response
时使用,期望此最后一次调用会产生'URLError'异常。
test_main.py
Query
上面的测试给出以下错误:
make_request_and_get_response
即使我捕获了import pytest
from unittest.mock import patch
from foo.query import Query
from foo.main import get_response_from_external_api
class TestExternalApiCall:
@patch('foo.query.Query')
def test_url_error(self, mockedQuery):
with patch('foo.query.Query.make_request_and_get_response', side_effect=Exception('URLError')):
with pytest.raises(Exception) as excinfo:
q= mockedQuery()
foo.main.get_response_from_external_api(q)
assert excinfo.value = 'URLError'
# assert excinfo.value.message == 'URLError' # this gives object has no attribute 'message'
异常,然后在> foo.main.get_response_from_external_api(q)
E Failed: DID NOT RAISE <class 'Exception'> id='72517784'>") == 'URLError'
中重新引发它,也会发生相同的错误。
有人可以帮助我理解我所缺少的内容,以便能够在pytest中引发异常吗?
更新:
如果我修改'URLError'
以删除get_response_from_external_api
情况:
main.py
然后在except Excpetion
中进行测试:
def get_response_from_external_api(query):
try:
response = query.make_request_and_get_response()
except urllib.error.URLError as e:
print('Got a URLError: ', e)
except urllib.error.HTTPError as e:
print('Got a HTTPError: ', e)
测试通过了。
答案 0 :(得分:1)
问题在于get_response_from_external_api()
已经捕获了Exception
并且没有将其引发到该函数之外的任何地方。因此,当您模拟查询并使make_request_and_get_response
引发异常时,pytest将不会看到该异常,因为get_response_from_external_api()
已经捕获了它。
如果将其更改为以下内容,则pytest有机会看到它:
except Exception:
print('Got a generic Exception!')
# handle this exception
raise
测试用例似乎也无法按预期工作。可以简化为以下内容(直接创建模拟查询并传递该查询要容易一些):
import pytest
from unittest import mock
from foo.main import get_response_from_external_api
class TestExternalApiCall:
def test_url_error(self):
mock_query = mock.Mock()
mock_query.make_request_and_get_response.side_effect = Exception('URLError')
with pytest.raises(Exception) as excinfo:
get_response_from_external_api(mock_query)
assert str(excinfo.value) == 'URLError'
然后测试用例通过(除了也用raise
进行了上述更改)。
回答问题1:
区别在于,您正在使用装饰器来模拟Query
,但是随后在测试用例中模拟了类foo.query.Query
以引发该异常。但是mockedQuery
丝毫没有改变,实际上对该方法所做的任何更改。因此q
是mock.Mock()
的常规实例,没有任何特殊之处。
您可以将其更改为以下内容(类似于上述方法):
import pytest
from unittest.mock import patch
from foo.query import Query
from foo.main import get_response_from_external_api
class TestExternalApiCall:
@patch('foo.query.Query')
def test_url_error(self, mockedQuery):
with patch.object(mockedQuery, 'make_request_and_get_response', side_effect=Exception('URLError')):
with pytest.raises(Exception) as excinfo:
get_response_from_external_api(mockedQuery)
assert str(excinfo.value) == 'URLError'
如果您的with patch('foo.query.Query........
语句在代码中的任何地方都可以工作,然后从该位置实例化一个Query
实例。
回答问题2:
嗯,我无法重现-您在本地拥有的测试用例与问题中的相同吗?由于foo.main.get_response_from_external_api(q)
不存在(foo
未导入),我不得不对其进行了修改,因此我将其更改为调用get_response_from_external_api(q)
,并且不断得到:
> get_response_from_external_api(q)
E Failed: DID NOT RAISE <class 'Exception'>