【问题标题】:Mocking two functions with patch for a unit test使用补丁模拟两个函数以进行单元测试
【发布时间】:2013-03-23 16:55:17
【问题描述】:

我有一个要单元测试的函数包含调用其他两个函数。我不确定如何使用补丁正确地同时模拟这两个函数。我在下面提供了一个例子来说明我的意思。当我运行nosetests时,测试通过了,但我觉得必须有一种更清洁的方法来做到这一点,我并不真正理解关于 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

【问题讨论】:

  • 我认为我最初的问题不清楚,所以我清理了它。我希望这能更准确地显示我正在寻找的东西!

标签: python unit-testing testing mocking nose


【解决方案1】:

您可以通过使用补丁装饰器并像这样嵌套它们来简化测试(默认情况下它们是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()

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

【讨论】:

  • 在您的建议中,您引入了两个参数,一个用于每个模拟。 python如何知道哪个是哪个?我没能在文档中找到答案。
  • 装饰器是自下而上应用的,参数的顺序需要与之匹配。见这里:voidspace.org.uk/python/mock/…
  • 是的,我已经读过这篇文章,但还不够清楚。谢谢!
  • 请注意,此处的参数与应用补丁的顺序相反。
  • 太棒了!谢谢你。
【解决方案2】:

除了响应@Matti John,您还可以在函数test_write_out 内使用patch

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
        ...

【讨论】:

    【解决方案3】:

    这里有一个简单的例子,说明如何使用 mock 在 create_collection 函数中测试提升 ConflictError

    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')
    

    另请参阅 mock docs 和 Michael Foord 的精彩 introduction to mock

    【讨论】:

    • 感谢您甚至试图帮助我。这确实对我有帮助,但我更专注于如何使用补丁模拟多个函数。不幸的是,我的问题没有说清楚。我现在已经清理了这个问题。
    猜你喜欢
    • 2017-02-10
    • 2017-11-15
    • 1970-01-01
    • 2019-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多