Я хотел бы написать несколько тестовых примеров для реализации object_check в логике isinstance (obj, requests.Response). После того, как я создаю данные Mock как возвращаемое значение для request.post. Тип для данных mock всегда является классом Mock. Таким образом, как я могу переписать макет данных, поэтому макет данных может быть типом запросов. Ответ? поэтому я могу d = obj.json()
линию 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
чтобы удерживать возвращаемые значения). Mock
также может создать ложное чувство безопасности (ваш тест будет продолжать проходить, даже если веб-службы резко меняются, поэтому вы можете взорваться в prod). Я не думаю, что вам это действительно нужно. Две альтернативы:
pickle
и сохранить на диск (сохранить его в своем тестовом наборе). Затем проверьте, чтобы ваш модуль тестировал его и использовал фактический объект ответа. Вам все равно придется patch
на requests.post
, но по крайней мере возвращаемые значения будут выстроены для вас, и вам не придется добавлять или изменять их по мере роста потребностей/приложений.patch
полностью: просто выполните POST в своем тесте и проверьте ответ. Конечно, это может быть медленным и работать будет только в том случае, если у вас есть интернет. И вы получите пугающих пуристов, которые скажут вам, чтобы вы никогда не делали этого в единичном тесте. Может быть, переместите его на тест интеграции, если вы столкнетесь с одним из этих пуристых людей. Но серьезно, нет никакой замены для того, чтобы делать то, что вы на самом деле собираетесь делать в prod. Положительным моментом является то, что если веб-сервис изменится, вы сразу узнаете об этом и сможете исправить свой код. Недостатком является то, что он может замедлить ваш тестовый пакет, и это потенциально ненадежный тест (если веб-сервис не работает, ваш тест не удастся... но на самом деле было бы хорошо это знать). Я рекомендую, если веб-служба нестабильна (т.е. Может измениться), используйте опцию 2. Else, используйте опцию 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
isinstance(m, request.Response)
== Ложь? это мой вопрос
requests.Response
(кроме request.Response
вы написали; я предполагаю, что это только опечатка). Существует класс requests.models.Response
, который экспортируется в requests.__init__.py
через __all__
. Таким образом, isinstance(m, requests.Response)
вернет True
.
pickle.dump(your_object, your_file_descriptor)
иpickle.load("your_filename")
. Вы можете увидеть больше здесь: stackoverflow.com/questions/4530611/… и документы для pickle здесь: docs.python.org/3/library/pickle.html . Вы действительно захотите включитьelif isinstance(obj, requests.Response):
, а затем вставить логику дампа сразу послеelif isinstance(obj, requests.Response):
Точно так же, когда вы делаете тест, вы читаете этиrequests.Response
объект обратно с диска и передаете его везде, где вы хотите запустить модульные тесты, которые нуждаются в нем как в зависимости.