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

Hus*_*ain 6 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
Run Code Online (Sandbox Code Playgroud)

我试图嘲弄urllib2.urlopenunittest上述模块.我已经写了

# -*- 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'
Run Code Online (Sandbox Code Playgroud)

运行unittest后,我得到一个例外

<urlopen error unknown url type: <MagicMock name='Request().get_type()' id='159846220'>>
Run Code Online (Sandbox Code Playgroud)

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

Mic*_*ico 9

你正在修补错误的东西:看看补丁的位置.

api.py通过

from urllib2 import urlopen
from urllib2 import Request
Run Code Online (Sandbox Code Playgroud)

您创建一个本地引用urlopen,并Request在文件中.通过mock.patch('urllib2.urlopen')要修补原来的参考和离开api.py的一个不变.

所以,替换你的补丁

@mock.patch('api.Request')
@mock.patch('api.urlopen')
Run Code Online (Sandbox Code Playgroud)

应该解决你的问题....但还不够.

在您的测试情况下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')
Run Code Online (Sandbox Code Playgroud)

现在澄清......在你的测试中你没有调用Request()因为你只传递了第一个参数,所以我删除了无用的补丁.此外,您正在修补urlopen函数而不是urlopen对象,这意味着read()您想要模拟的方法是对象通过urlopen()调用返回的方法.

最后,我添加了一个urlopen电话检查,autospec=True这总是一个很好的做法.