使用一个单元测试的补丁模仿两个函数

我有一个函数,我想单元测试包含调用两个其他函数。我不确定如何能够同时正确地使用补丁模拟这两个功能。我举了一个例子来说明我在下面的意思。当我运行 nosetest 时,测试通过了,但是我觉得必须有一个更干净的方法来做这件事,而且我并不真正理解关于 f.close ()的部分..。

目录结构如下:

program/
program/
data.py
tests/
data_test.py

Py:

import cPickle


def write_out(file_path, data):
f = open(file_path, 'wb')
cPickle.dump(data, f)
f.close()

Data _ test. py:

from mock import MagicMock, patch


def test_write_out():
path = '~/collection'
mock_open = MagicMock()
mock_pickle = MagicMock()
f_mock = MagicMock()
with patch('__builtin__.open', mock_open):
f = mock_open.return_value
f.method.return_value = path
with patch('cPickle.dump', mock_pickle):
write_out(path, 'data')
mock_open.assert_called_once_with('~/collection', 'wb')
f.close.assert_any_call()
mock_pickle.assert_called_once_with('data', f)

结果:

$ nosetests
.
----------------------------------------------------------------------
Ran 1 test in 0.008s
OK
99500 次浏览

Here's a simple example on how to test raising ConflictError in create_collection function using mock:

import os
from unittest import TestCase
from mock import patch
from ..program.data import ConflictError, create_collection




class TestCreateCollection(TestCase):
def test_path_exists(self):
with patch.object(os.path, 'exists') as mock_method:
mock_method.return_value = True


self.assertRaises(ConflictError, create_collection, 'test')

Please, also see mock docs and Michael Foord's awesome introduction to mock.

You can simplify your test by using the patch decorator and nesting them like so (they are MagicMock objects by default):

from unittest.mock import patch


@patch('cPickle.dump')
@patch('__builtin__.open')
def test_write_out(mock_open, mock_pickle):
path = '~/collection'
f = mock_open.return_value
f.method.return_value = path
    

write_out(path, 'data')
    

mock_open.assert_called_once_with('~/collection', 'wb')
mock_pickle.assert_called_once_with('data', f)
f.close.assert_any_call()

Calls to a MagicMock instance return a new MagicMock instance, so you can check that the returned value was called just like any other mocked object. In this case f is a MagicMock named 'open()' (try printing f).

In addition to the response @Matti John you can also use patch inside function test_write_out:

from mock import MagicMock, patch


def test_write_out():
path = '~/collection'
with patch('__builtin__.open') as mock_open, \
patch('cPickle.dump') as mock_pickle:


f = mock_open.return_value
...

As of Python 3.10 you can do use Parenthesized Context Managers like this

from unittest.mock import patch




def test_write_out():
with (
patch('cPickle.dump'),
patch('__builtin__.open') as open_mock,  # example of using `as`
):
yield