我需要创建一个模拟REST API失败调用的单元测试,从PyActiveResource项目(https://github.com/Shopify/pyactiveresource)返回一个UnauthorizedAccess异常的副作用,并将其存储在DB中。到目前为止,我所创造的东西是有效的,而且我得到了预期的副作用。然后,我在函数foo. function_that_call_myfunction()上捕获它,它看起来像这样my_func.py:
from pyactiveresource.connection import UnauthorizedAccess
class MyFuncAnyThing:
...
def function_that_call_myfuncion(self, attr=None):
try:
module.MyTestClass.myfunction(attr)
except UnauthorizedAccess as error:
#Catch the error response object and store it on DB
resp_body = error.response.body
resp_code = error.response.code
#store it on DB
...
我的测试文件是这样的unit_test.py:
from pyactiveresource.connection import UnauthorizedAccess
class TestFoo(TestCase):
def test_exception_unauthorized_access(self):
foo = SomeThingFactory()
with patch('module.MyTestClass.myfunction', side_effect=UnauthorizedAccess()):
foo.function_that_call_myfuncion()
#assertions goes below here
...
因此,当执行到达my_function .py模块的function_that_call_myfuncion
上的try块时,模拟函数返回所需的异常(UnauthorizedAccess),返回的对象看起来像这样:
error
UnauthorizedAccess('Response(code=None, body="", headers={}, msg="")')
当我试图模拟响应体时,我的问题开始了UnauthorizedAccess异常返回。这就是我正在做的:unit_test.py:
from pyactiveresource.connection import UnauthorizedAccess
class TestFoo(TestCase):
def test_exception_unauthorized_access(self):
foo = SomeThingFactory()
bar = MagicMock()
bar.code = 401
bar.body = '{"errors": "Login or wrong password"}'
with patch('module.MyTestClass.myfunction', side_effect=UnauthorizedAccess(bar)):
foo.function_that_call_myfuncion()
#assertions goes below here
...
这就是模拟对象的样子:
error
UnauthorizedAccess('Response(code=401, body="<MagicMock name='mock.read()' id='2243840764512'>", headers={}, msg="<MagicMock name='mock.msg' id='2243840808464'>")')
注意Response上的code属性是401
,但是body是空的,尽管我在这里设置了bar.body = '{"errors": "Login or wrong password"}'
。我还尝试创建一个响应的子类UnauthorizedAccess类的构造函数将其作为参数传递给pyactiverresource的class ConnectionError(Error):
。连接库代码(https://github.com/Shopify/pyactiveresource/blob/e609d844ebace603f74bc5f0a67e9eafe7fb25e1/pyactiveresource/connection.py#L34)
unit_test.py:
from pyactiveresource.connection import UnauthorizedAccess, Response
class TestFoo(TestCase):
def test_exception_unauthorized_access(self):
foo = SomeThingFactory()
resp = Response(code=401,body='{"errors": "Login or wrong password"}')
with patch('module.MyTestClass.myfunction', side_effect=UnauthorizedAccess(response=resp)):
foo.function_that_call_myfuncion()
#assertions goes below here
...
但是我从类响应中得到了这个错误:
@classmethod
def from_httpresponse(cls, response):
"""Create a Response object based on an httplib.HTTPResponse object.
Args:
response: An httplib.HTTPResponse object.
Returns:
A Response object.
"""
> return cls(response.code, response.read(),
dict(response.headers), response.msg, response)
E AttributeError: 'Response' object has no attribute 'read'
我错过了什么?我只是不知道如何在构造函数上设置'read'属性,以便我可以获得主体值。
我使用Python 3.8, Django 2.2
我通过做一些事情来模仿Shopify的ClientError
异常:
import urllib.error
from io import BytesIO
import pyactiveresource.testing.http_fake
pyactiveresource.testing.http_fake.initialize()
response = urllib.error.HTTPError('', 401, '', {}, BytesIO(b''))
pyactiveresource.testing.http_fake.TestHandler.set_response(response)
这是我通过挖掘Shopify/pyactiverresource测试了解到的。