Python 3 urlopen上下文管理器嘲笑

Yev*_*evs 7 testing unit-testing python-3.x

我是新手测试,需要一些帮助.

假设有这种方法:

from urllib.request import urlopen

def get_posts():
    with urlopen('some url here') as data:
        return json.loads(data.read().decode('utf-8'))
Run Code Online (Sandbox Code Playgroud)

问题是如何测试这个方法(如果可能的话,使用mock.patch装饰器)?

我现在拥有的:

@mock.patch('mymodule.urlopen')
def test_get_post(self, mocked_urlopen):
    mocked_urlopen.__enter__ = Mock(return_value=self.test_data)
    mocked_urlopen.__exit__ = Mock(return_value=False)
    ...
Run Code Online (Sandbox Code Playgroud)

但它似乎没有奏效.

PS有没有方便的方法在测试中使用数据变量(哪种类型是HTTPResponse),所以它可能只是简单的字符串?

小智 8

我也在与此作斗争,最后弄清楚了。(Python 3语法):

import urllib.request
import unittest
from unittest.mock import patch, MagicMock

class TestUrlopen(unittest.TestCase):
    @patch('urllib.request.urlopen')
    def test_cm(self, mock_urlopen):
        cm = MagicMock()
        cm.getcode.return_value = 200
        cm.read.return_value = 'contents'
        cm.__enter__.return_value = cm
        mock_urlopen.return_value = cm

        with urllib.request.urlopen('http://foo') as response:
            self.assertEqual(response.getcode(), 200)
            self.assertEqual(response.read(), 'contents')

    @patch('urllib.request.urlopen')
    def test_no_cm(self, mock_urlopen):
        cm = MagicMock()
        cm.getcode.return_value = 200
        cm.read.return_value = 'contents'
        mock_urlopen.return_value = cm

        response = urllib.request.urlopen('http://foo')
        self.assertEqual(response.getcode(), 200)
        self.assertEqual(response.read(), 'contents')
        response.close()
Run Code Online (Sandbox Code Playgroud)


Yev*_*evs -1

好的,所以我编写了简单的类来模拟上下文管理器。

class PatchContextManager:

    def __init__(self, method, enter_return, exit_return=False):
        self._patched = patch(method)
        self._enter_return = enter_return
        self._exit_return = exit_return

    def __enter__(self):
        res = self._patched.__enter__()
        res.context = MagicMock()
        res.context.__enter__.return_value = self._enter_return
        res.context.__exit__.return_value = self._exit_return
        res.return_value = res.context
        return res

    def __exit__(self, type, value, tb):
        return self._patched.__exit__()
Run Code Online (Sandbox Code Playgroud)

用法:

with PatchContextManager('mymodule.method', 'return_string') as mocked:
    a = mymodule.method(47) # a == 'return_string'
    mocked.assert_called_with(47)
    ... 
Run Code Online (Sandbox Code Playgroud)