无法使用Python的mock.patch来模拟urllib2.urlopen

时间:2015-03-31 09:34:09

标签: python unit-testing urllib2 python-mock

以下是我的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'>>

我错过了什么?请帮帮我。

1 个答案:

答案 0 :(得分:9)

你正在修补错误的东西:看看Where to patch

api.py

from urllib2 import urlopen
from urllib2 import Request

您在文件中创建了对urlopenRequest的本地引用。在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的检查,这始终是一种很好的做法。