简体   繁体   English

如何在 Python 3.5 中使用 unittest.mock 模拟导入的库方法?

[英]How to mock imported library methods with unittest.mock in Python 3.5?

Is it possible to mock methods of imported modules with unittest.mock in Python 3.5?是否可以在 Python 3.5 中使用 unittest.mock 模拟导入模块的方法?

# file my_function.py
import os
my_function():
    # do something with os, e.g call os.listdir
    return os.listdir(os.getcwd())

# file test_my_function.py
test_my_function():
    os = MagickMock()
    os.listdir = MagickMock(side_effect=["a", "b"])
    self.assertEqual("a", my_function())

I expected that the the os.listdir method returns the specified side_effect "a" on the first call, but inside of my_function the unpatched os.listdir is called.我希望 os.listdir 方法在第一次调用时返回指定的 side_effect "a",但在 my_function 内部调用了未修补的 os.listdir。

unittest.mock have two main duties: unittest.mock有两个主要职责:

  • Define Mock objects: object designed to follow your screenplay and record every access to your mocked object定义Mock对象:旨在遵循您的剧本并记录对Mock对象的每次访问的对象
  • patching references and recover the original state修补引用并恢复原始状态

In your example, you need both functionalities: Patching os.listdir reference used in production code by a mock where you can have complete control of how it will respond.在您的示例中,您需要两种功能: 修补生产代码中使用的模拟os.listdir引用,您可以在其中完全控制它的响应方式。 There are a lot of ways to use patch , some details to take care on how use it and cavelets to know .有很多方法可以使用patch ,一些细节需要注意如何使用它以及要知道的小窝

In your case you need to test my_function() behaviour and you need to patch both os.listdir() and os.getcwd() .在您的情况下,您需要测试my_function()行为,并且需要修补os.listdir()os.getcwd() Moreover what you need is control the return_value (take a look to the pointed documentation for return_value and side_effect differences).此外,您需要的是控制return_value (查看有关return_valueside_effect差异的有针对性的文档)。

I rewrote your example a little bit to make it more complete and clear:我稍微重写了你的例子,使其更完整和清晰:

my_function(nr=0):
    l = os.listdir(os.getcwd())
    l.sort()
    return l[nr]

@patch("os.getcwd")
@patch("os.listdir", return_value=["a","b"])
def test_my_function(mock_ld, mock_g): 
    self.assertEqual("a", my_function(0))
    mock_ld.assert_called_with(mock_g.return_value)
    self.assertEqual("a", my_function())
    self.assertEqual("b", my_function(1))
    with self.assertRaises(IndexError):
        my_function(3)

I used the decorator syntax because I consider it the cleaner way to do it;我使用装饰器语法是因为我认为它是一种更简洁的方法; moreover, to avoid the introduction of too many details I didn't use autospecing that I consider a very best practice.此外,为了避免引入太多细节,我没有使用我认为是最佳实践的自动指定。

Last note: mocking is a powerful tool but use it and not abuse of it, patch just you need to patch and nothing more.最后一点:mocking 是一个强大的工具,但请使用它而不是滥用它,修补只是你需要修补而已,仅此而已。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM