如何将数据模拟为 request.Response 输入 python
How to mock data as request.Response type in python
我想写一些测试用例来练习 isinstance(obj, requests.Response) 中的 object_check 逻辑。在我为 requests.post 创建模拟数据作为 return 值之后。模拟数据的类型始终是 Mock class。这样,我怎样才能重写模拟数据,使模拟数据可以是 requests.Response 类型?这样我就可以练习行 d = obj.json()
?
from unittest.mock import patch, Mock
import unittest
import requests
from requests.exceptions import HTTPError
import pytest
def object_check(obj):
if isinstance(obj, bytes):
d = ujson.decode(obj.decode())
elif isinstance(obj, requests.Response):
d = obj.json()
else:
raise ValueError('invalid type')
return d
def service_post(params):
"""
trivial function that does a GET request
against google, checks the status of the
result and returns the raw content
"""
url = "https://www.iamdomain.com"
params = {'number': 1234, 'user_id': 1, 'name': 'john'}
resp = requests.post(url, data=params)
return object_check(resp)
@patch.object(requests, 'post')
def test_service_post(mock_request_post):
data = {'number': 0000, 'user_id': 0, 'name': 'john'}
def res():
r = Mock()
r.status_code.return_value = 200
r.json.return_value = data
return r
mock_request_post.return_value = res()
assert data == service_post(data)
你可以这样做:
@patch.object(requests, 'post')
def test_service_post(mock_request_post):
data = {'number': 0000, 'user_id': 0, 'name': 'john'}
def res():
r = requests.Response()
r.status_code = 200
def json_func():
return data
r.json = json_func
return r
mock_request_post.return_value = res()
assert data == service_post(data)
当我在本地 运行 时,测试通过了。 请注意 Mock 是一种迷你气味。
我曾经是Mock
的忠实粉丝。但是,随着我作为一名开发人员的成长,我真的会尽量避免它。它可能会诱使您进行一些非常糟糕的设计,并且它们可能真的很难维护(特别是因为您正在修改 Mock
以保存 return 值)。 Mock
还会产生一种错误的安全感(即使 Web 服务发生巨大变化,您的测试仍会继续通过,因此您可能会在生产中爆炸)。我认为您在这里真的不需要它。两种选择:
- 您可以点击您尝试点击的任何服务,并使用
pickle
序列化(保存)该响应,然后存储到磁盘(将其保存在您的测试套件中)。然后让您的单元测试读回它并使用实际的响应对象。您仍然需要 patch
超过 requests.post
,但至少 return 值会为您排列,您不必添加或修改它们作为您的 [=38] =] 增长。
- 刚刚上网。完全忘记
patch
:只需在测试中执行 POST 并检查响应。当然,这可能会很慢,并且只有在有互联网的情况下才能工作。而且你会遇到愚蠢的纯粹主义者,他们会告诉你永远不要在单元测试中这样做。如果您 运行 成为那些纯粹的人之一,也许将其移至集成测试。但说真的,没有什么可以替代你在生产中实际要做的事情。这样做的好处是,如果 Web 服务发生变化,您会立即知道并修复您的代码。缺点是它会减慢您的测试套件,并且它可能是一个不可靠的测试(如果 web 服务关闭,您的测试将失败......但知道这一点实际上可能是件好事)。
我建议如果网络服务不稳定(即容易更改),请使用选项 2。否则,请使用选项 1。或者将两者结合使用(Mock
和 patch
用于一个单元测试,并在集成测试中点击服务)。只有你能决定!
HTH,祝你好运!
实例化模拟时使用 spec
参数:
>>> from unittest.mock import Mock
>>> from requests import Response
>>> m = Mock(spec=Response)
>>> m.__class__
requests.models.Response
>>> isinstance(m, Response)
True
另请注意,r.status_code.return_value = 200
不适用于 speccing;直接设置值:
r.status_code = 200
如果您想模拟 text
或 content
@属性 值,请在 text
周围使用 PropertyMock
@patch.object(requests, 'post')
def test_service_post(mock_request_post):
data = {'number': 0000, 'user_id': 0, 'name': 'john'}
def res():
r = requests.Response()
r.status_code = 200
type(r).text = mock.PropertyMock(return_value=my_text) # property mock
def json_func():
return data
r.json = json_func
return r
mock_request_post.return_value = res()
assert data == service_post(data)
我想写一些测试用例来练习 isinstance(obj, requests.Response) 中的 object_check 逻辑。在我为 requests.post 创建模拟数据作为 return 值之后。模拟数据的类型始终是 Mock class。这样,我怎样才能重写模拟数据,使模拟数据可以是 requests.Response 类型?这样我就可以练习行 d = obj.json()
?
from unittest.mock import patch, Mock
import unittest
import requests
from requests.exceptions import HTTPError
import pytest
def object_check(obj):
if isinstance(obj, bytes):
d = ujson.decode(obj.decode())
elif isinstance(obj, requests.Response):
d = obj.json()
else:
raise ValueError('invalid type')
return d
def service_post(params):
"""
trivial function that does a GET request
against google, checks the status of the
result and returns the raw content
"""
url = "https://www.iamdomain.com"
params = {'number': 1234, 'user_id': 1, 'name': 'john'}
resp = requests.post(url, data=params)
return object_check(resp)
@patch.object(requests, 'post')
def test_service_post(mock_request_post):
data = {'number': 0000, 'user_id': 0, 'name': 'john'}
def res():
r = Mock()
r.status_code.return_value = 200
r.json.return_value = data
return r
mock_request_post.return_value = res()
assert data == service_post(data)
你可以这样做:
@patch.object(requests, 'post')
def test_service_post(mock_request_post):
data = {'number': 0000, 'user_id': 0, 'name': 'john'}
def res():
r = requests.Response()
r.status_code = 200
def json_func():
return data
r.json = json_func
return r
mock_request_post.return_value = res()
assert data == service_post(data)
当我在本地 运行 时,测试通过了。 请注意 Mock 是一种迷你气味。
我曾经是Mock
的忠实粉丝。但是,随着我作为一名开发人员的成长,我真的会尽量避免它。它可能会诱使您进行一些非常糟糕的设计,并且它们可能真的很难维护(特别是因为您正在修改 Mock
以保存 return 值)。 Mock
还会产生一种错误的安全感(即使 Web 服务发生巨大变化,您的测试仍会继续通过,因此您可能会在生产中爆炸)。我认为您在这里真的不需要它。两种选择:
- 您可以点击您尝试点击的任何服务,并使用
pickle
序列化(保存)该响应,然后存储到磁盘(将其保存在您的测试套件中)。然后让您的单元测试读回它并使用实际的响应对象。您仍然需要patch
超过requests.post
,但至少 return 值会为您排列,您不必添加或修改它们作为您的 [=38] =] 增长。 - 刚刚上网。完全忘记
patch
:只需在测试中执行 POST 并检查响应。当然,这可能会很慢,并且只有在有互联网的情况下才能工作。而且你会遇到愚蠢的纯粹主义者,他们会告诉你永远不要在单元测试中这样做。如果您 运行 成为那些纯粹的人之一,也许将其移至集成测试。但说真的,没有什么可以替代你在生产中实际要做的事情。这样做的好处是,如果 Web 服务发生变化,您会立即知道并修复您的代码。缺点是它会减慢您的测试套件,并且它可能是一个不可靠的测试(如果 web 服务关闭,您的测试将失败......但知道这一点实际上可能是件好事)。
我建议如果网络服务不稳定(即容易更改),请使用选项 2。否则,请使用选项 1。或者将两者结合使用(Mock
和 patch
用于一个单元测试,并在集成测试中点击服务)。只有你能决定!
HTH,祝你好运!
实例化模拟时使用 spec
参数:
>>> from unittest.mock import Mock
>>> from requests import Response
>>> m = Mock(spec=Response)
>>> m.__class__
requests.models.Response
>>> isinstance(m, Response)
True
另请注意,r.status_code.return_value = 200
不适用于 speccing;直接设置值:
r.status_code = 200
如果您想模拟 text
或 content
@属性 值,请在 text
PropertyMock
@patch.object(requests, 'post')
def test_service_post(mock_request_post):
data = {'number': 0000, 'user_id': 0, 'name': 'john'}
def res():
r = requests.Response()
r.status_code = 200
type(r).text = mock.PropertyMock(return_value=my_text) # property mock
def json_func():
return data
r.json = json_func
return r
mock_request_post.return_value = res()
assert data == service_post(data)