如何用unittest.mock测试下面的代码:

def testme(filepath):
    with open(filepath) as f:
        return f.read()

当前回答

我可能对这个游戏有点晚了,但当我在另一个模块中调用open而不必创建新文件时,这对我来说是有效的。

test.py

import unittest
from mock import Mock, patch, mock_open
from MyObj import MyObj

class TestObj(unittest.TestCase):
    open_ = mock_open()
    with patch.object(__builtin__, "open", open_):
        ref = MyObj()
        ref.save("myfile.txt")
    assert open_.call_args_list == [call("myfile.txt", "wb")]

MyObj.py

class MyObj(object):
    def save(self, filename):
        with open(filename, "wb") as f:
            f.write("sample text")

通过将__builtin__模块中的open函数修补到mock_open()中,我可以模拟写入文件而无需创建一个文件。

注意:如果你正在使用一个使用cython的模块,或者你的程序以任何方式依赖于cython,你将需要导入cython的__builtin__模块,方法是在你的文件顶部包含import __builtin__。如果你正在使用cython,你将不能模拟通用__builtin__。

其他回答

如果你不需要进一步的文件,你可以装饰测试方法:

@patch('builtins.open', mock_open(read_data="data"))
def test_testme():
    result = testeme()
    assert result == "data"

我可能对这个游戏有点晚了,但当我在另一个模块中调用open而不必创建新文件时,这对我来说是有效的。

test.py

import unittest
from mock import Mock, patch, mock_open
from MyObj import MyObj

class TestObj(unittest.TestCase):
    open_ = mock_open()
    with patch.object(__builtin__, "open", open_):
        ref = MyObj()
        ref.save("myfile.txt")
    assert open_.call_args_list == [call("myfile.txt", "wb")]

MyObj.py

class MyObj(object):
    def save(self, filename):
        with open(filename, "wb") as f:
            f.write("sample text")

通过将__builtin__模块中的open函数修补到mock_open()中,我可以模拟写入文件而无需创建一个文件。

注意:如果你正在使用一个使用cython的模块,或者你的程序以任何方式依赖于cython,你将需要导入cython的__builtin__模块,方法是在你的文件顶部包含import __builtin__。如果你正在使用cython,你将不能模拟通用__builtin__。

在我的例子中,我使用的是pytest,好消息是在Python 3中,单元测试库也可以导入和使用,没有问题。

以下是我的方法。首先,我用可重用的pytest fixture创建了一个conftest.py文件:

from functools import cache
from unittest.mock import MagicMock, mock_open

import pytest
from pytest_mock import MockerFixture


class FileMock(MagicMock):

    def __init__(self, mocker: MagicMock = None, **kwargs):
        super().__init__(**kwargs)

        if mocker:
            self.__dict__ = mocker.__dict__
            # configure mock object to replace the use of open(...)
            # note: this is useful in scenarios where data is written out
            _ = mock_open(mock=self)

    @property
    def read_data(self):
        return self.side_effect

    @read_data.setter
    def read_data(self, mock_data: str):
        """set mock data to be returned when `open(...).read()` is called."""
        self.side_effect = mock_open(read_data=mock_data)

    @property
    @cache
    def write_calls(self):
        """a list of calls made to `open().write(...)`"""
        handle = self.return_value
        write: MagicMock = handle.write
        return write.call_args_list

    @property
    def write_lines(self) -> str:
        """a list of written lines (as a string)"""
        return ''.join([c[0][0] for c in self.write_calls])


@pytest.fixture
def mock_file_open(mocker: MockerFixture) -> FileMock:
    return FileMock(mocker.patch('builtins.open'))

在这里,我决定将read_data作为一个属性,以便更python化。它可以用open()需要返回的任何数据在测试函数中赋值。

在我的测试文件中,命名为test_it_works.py,我有以下测试用例来确认预期的功能:

from unittest.mock import call


def test_mock_file_open_and_read(mock_file_open):
    mock_file_open.read_data = 'hello\nworld!'

    with open('/my/file/here', 'r') as in_file:
        assert in_file.readlines() == ['hello\n', 'world!']

    mock_file_open.assert_called_with('/my/file/here', 'r')


def test_mock_file_open_and_write(mock_file_open):
    with open('/out/file/here', 'w') as f:
        f.write('hello\n')
        f.write('world!\n')
        f.write('--> testing 123 :-)')

    mock_file_open.assert_called_with('/out/file/here', 'w')

    assert call('world!\n') in mock_file_open.write_calls

    assert mock_file_open.write_lines == """\
hello
world!
--> testing 123 :-)
""".rstrip()

点击这里查看要点。

使用assert简单的@patch

如果你想使用@patch。open()在处理程序内部被调用并被读取。

    @patch("builtins.open", new_callable=mock_open, read_data="data")
    def test_lambda_handler(self, mock_open_file):
        
        lambda_handler(event, {})

要将mock_open用于一个简单的文件read()(本页上已经给出的原始mock_open片段更适合于write):

my_text = "some text to return when read() is called on the file object"
mocked_open_function = mock.mock_open(read_data=my_text)

with mock.patch("__builtin__.open", mocked_open_function):
    with open("any_string") as f:
        print f.read()

注意,根据mock_open的文档,这是专门针对read()的,所以不会适用于常见的模式,例如for line in f。

使用python 2.6.6 / mock 1.0.1