Python 用补丁模拟两个函数进行单元测试

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15763394/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 20:56:03  来源:igfitidea点击:

Mocking two functions with patch for a unit test

pythonunit-testingtestingmockingnose

提问by cnodell

I have a function I want to unit test contains calls two other functions. I am unsure how can I mock both functions at the same time properly using patch. I have provided an example of what I mean below. When I run nosetests, the tests pass but I feel that there must be a cleaner way to do this and I do not really Understand the piece regarding f.close()...

我有一个函数我想单元测试包含调用另外两个函数。我不确定如何正确使用补丁同时模拟这两个函数。我在下面提供了一个例子来说明我的意思。当我运行鼻子测试时,测试通过了,但我觉得必须有一种更简洁的方法来做到这一点,而且我并不真正理解关于 f.close() 的部分......

The directory structure looks like this:

目录结构如下所示:

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

data.py:

数据.py:

import cPickle

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

data_test.py:

数据测试.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)

Results:

结果:

$ nosetests
.
----------------------------------------------------------------------
Ran 1 test in 0.008s
OK

采纳答案by Matti John

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

您可以通过使用补丁装饰器并像这样嵌套它们来简化测试(MagicMock默认情况下它们是对象):

@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 MagicMockinstance return a new MagicMockinstance, so you can check that the returned value was called just like any other mocked object. In this case fis a MagicMocknamed 'open()'(try printing f).

调用MagicMock实例会返回一个新MagicMock实例,因此您可以像调用任何其他模拟对象一样检查返回值是否被调用。在这种情况下f是一个MagicMock命名'open()'(尝试打印f)。

回答by alecxe

Here's a simple example on how to test raising ConflictErrorin create_collectionfunction using mock:

这是一个关于如何使用模拟ConflictErrorcreate_collection函数中测试提升的简单示例:

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 docsand Michael Foord's awesome introduction to mock.

请同时参阅mock 文档和 Michael Foord对 mock的精彩介绍

回答by Alex Lisovoy

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

除了响应@Matti John 您还可以使用patch内部函数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
        ...