Невозможно высмеять urllib2.urlopen с помощью pockon mock.patch

Ниже приведен фрагмент кода моего модуля api.py

# -*- coding: utf-8 -*- from urllib2 import urlopen from urllib2 import Request class API: def call_api(self, url, post_data=None, header=None): is_post_request = True if (post_data and header) else False response = None try: if is_post_request: url = Request(url = url, data = post_data, headers = header) # Calling api api_response = urlopen(url) response = api_response.read() except Exception as err: response = err return response 

Я пытаюсь urllib2.urlopen в unittest выше модуля. Я написал

 # -*- coding: utf-8 -*- # test_api.py from unittest import TestCase import mock from api import API class TestAPI(TestCase): @mock.patch('urllib2.Request') @mock.patch('urllib2.urlopen') def test_call_api(self, urlopen, Request): urlopen.read.return_value = 'mocked' Request.get_host.return_value = 'google.com' Request.type.return_value = 'https' Request.data = {} _api = API() assert _api.call_api('https://google.com') == 'mocked' 

После запуска unittest я получаю исключение

 <urlopen error unknown url type: <MagicMock name='Request().get_type()' id='159846220'>> 

Что мне не хватает? Пожалуйста, помогите мне.

Вы исправляете неправильные вещи: посмотрите, где патч .

В api.py по

 from urllib2 import urlopen from urllib2 import Request 

вы создаете локальную ссылку на urlopen и Request в вашем файле. От mock.patch('urllib2.urlopen') вы исправляете исходную ссылку и оставляете api.py одним нетронутым.

Итак, замените свои патчи на

 @mock.patch('api.Request') @mock.patch('api.urlopen') 

должен исправить вашу проблему …. но этого недостаточно.

В вашем тестовом примере api.Request не используется, но urllib2.urlopen() создает Request с использованием исправленной версии: вот почему Request().get_type() – это MagicMock .

Для полного исправления вы должны полностью изменить свой тест. Сначала код:

 @mock.patch('api.urlopen', autospec=True) def test_call_api(self, urlopen): urlopen.return_value.read.return_value = 'mocked' _api = API() self.assertEqual(_api.call_api('https://google.com'), 'mocked') urlopen.assert_called_with('https://google.com') 

Теперь уточнение … В вашем тесте вы не вызываете Request() потому что вы передаете только первый параметр, поэтому я удалил бесполезный патч. Кроме того, вы исправляете функцию urlopen а не объект urlopen , это означает, что метод read() который вы хотите насмехаться, является методом возврата объекта по urlopen() .

Наконец, я добавляю чек на вызов urlopen и autospec=True который всегда является хорошей практикой.