【问题标题】:Patching a function with Mock only for one module?仅针对一个模块使用 Mock 修补功能?
【发布时间】:2012-09-19 05:15:58
【问题描述】:

我需要修补 os.listdir 和其他 os 函数来测试我的 Python 函数。但是当它们被修补时,import 语句会失败。是否可以仅在单个模块(真正的模块)内修补此功能,并让 tests.py 正常工作?

这是一个破坏import的例子:

import os
from mock import patch

# when both isdir and isfile are patched
# the function crashes
@patch('os.path.isdir', return_value=False)
@patch('os.path.isfile', return_value=False)
def test(*args):
    import ipdb; ipdb.set_trace()
    real_function(some_arguments)
    pass

test()

我希望real_function 看到修补过的os.path,并测试以查看正常功能。

here's the traceback

【问题讨论】:

    标签: python unit-testing python-mock


    【解决方案1】:

    您可以使用patch 作为上下文管理器,因此它只适用于with 语句中的代码:

    import os
    from mock import patch
    
    def test(*args):
        import ipdb; ipdb.set_trace()
        with patch('os.path.isdir', return_value=False):
            with patch('os.path.isfile', return_value=False):
                real_function(some_arguments)
        pass
    
    test()
    

    【讨论】:

    • 这是个好主意。实际上,我可以制作自己的上下文管理器。
    【解决方案2】:

    以下内容可满足您的需求。请注意,我在测试运行之前和之后打印 os.listdir 只是为了显示所有内容都返回到正确的状态。

    import unittest, os
    from unittest import TestLoader, TextTestRunner
    
    def tested_func(path):
        return os.listdir(path * 2)
    
    class Example(unittest.TestCase):
    
        def setUp(self):
            self._listdir = os.listdir
            os.listdir = lambda p: ['a', 'b', 'a', 'b']
    
        def test_tested_func(self):
            self.assertEqual(tested_func('some_path'), ['a', 'b', 'a', 'b'])
    
        def tearDown(self):
            os.listdir = self._listdir
    
    print os.listdir
    TextTestRunner().run(TestLoader().loadTestsFromTestCase(Example))
    print os.listdir
    

    【讨论】:

    • 你提供的只是模拟模块的简单模拟,它并没有解决主要问题:os.listdir 也被其他代码修补,导入失败。
    【解决方案3】:

    您可以在测试方法或测试类上使用补丁作为装饰器。它使代码看起来非常干净。请注意,已修补的对象被传递到测试方法中。您可以在测试类级别做同样的事情,模拟对象将被传递到每个测试方法中。在这种情况下不需要使用 setUp 或 tearDown 因为这一切都是自动处理的。

    # module.py
    import os
    
    def tested_func(path):
        return os.listdir(path * 2)
    
    
    # test.py
    from mock import patch
    
    @patch('module.os.listdir')
    def test_tested_func(self, mock_listdir):
        mock_listdir.return_value = ['a', 'b']
    
        self.assertEqual(tested_func('some_path'), ['a', 'b', 'a', 'b'])
        mock_listdir.assert_called_with('some_path' * 2)
    

    还要注意,您在要实际测试的模块中模拟了要修补的功能。

    【讨论】:

    • 您错过了重点:修补 os 文件系统功能块从修补后的代码导入。
    • 您能否发布更多代码,一个实际的突破性示例?因为我不知道从您给出的示例中这将如何起作用。很想尝试找出问题所在。
    • 回溯会很有趣
    • 我用最少的可崩溃代码和回溯更新了我的帖子。调试愉快! :)
    【解决方案4】:

    你不仅“可以”,而且“应该”这样做http://mock.readthedocs.org/en/latest/patch.html#where-to-patch

    p.s.:如果你想用更多“声明性”的方式来描述你想要修补的单元的副作用,我建议使用库http://mockstar.readthedocs.org/

    【讨论】:

    • 我阅读并尝试了这个。我按名称将函数导入本地命名空间,但修补后导入失败。
    猜你喜欢
    • 2011-07-17
    • 2014-08-29
    • 1970-01-01
    • 2012-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-02
    • 1970-01-01
    相关资源
    最近更新 更多