【问题标题】:mock/patch os.path.exists with multiple return values模拟/补丁 os.path.exists 具有多个返回值
【发布时间】:2014-03-22 12:46:39
【问题描述】:

我正在尝试测试我创建的一个函数,它遍历一个列表,并为列表中的每个项目调用os.path.exists。我的测试是向函数传递 2 个对象的列表。我需要os.path.exists 为其中一个返回True,为另一个返回False。我试过这个:

import mock
import os
import unittest

class TestClass(unittest.TestCase):
    values = {1 : True, 2 : False}
    def side_effect(arg):
        return values[arg]

    def testFunction(self):
        with mock.patch('os.path.exists') as m:
            m.return_value = side_effect # 1
            m.side_effect = side_effect # 2

            arglist = [1, 2]
            ret = test(argList)

使用第 1 行和第 2 行中的任何一个但不能同时使用会得到 NameError: global name 'side_effect' is not defined

我找到了这个question 并像这样修改了我的代码:

import mock
import os

class TestClass(unittest.TestCase):
    values = {1 : True, 2 : False}
    def side_effect(arg):
        return values[arg]

    def testFunction(self):
        mockobj = mock(spec=os.path.exists)
        mockobj.side_effect = side_effect

        arglist = [1, 2]
        ret = test(argList)

这会产生TypeError: 'module' object is not callable。 我也尝试切换这些行:

mockobj = mock(spec=os.path.exists)
mockobj.side_effect = side_effect

为此

mockobj = mock(spec=os.path)
mockobj.exists.side_effect = side_effect

还有这个

mockobj = mock(spec=os)
mockobj.path.exists.side_effect = side_effect

产生同样的错误。谁能指出我做错了什么以及我能做些什么来让它发挥作用?

编辑: 在下面发布我的答案后,我意识到我的第一段代码实际上也能正常工作,我只需要 m.side_effect = TestClass.side_effect 而不是 m.side_effect = side_effect

【问题讨论】:

  • 最好隔离问题并创建最小的工作示例 (MWE)。请学习 Python 中的作用域(我提供了链接,docs.python.org/3/reference/…)。这是 Python 中非常基本的事情,很抱歉投反对票。
  • 提供的TestClass不是MWE吗?
  • 也许你是对的,对于单元测试来说,这个类是一个 MWE。我使用 pytest,一个函数就足够了。但是我称它不是 MWE,因为有两个类,而不是一个。在第一个示例中,错误与模拟无关。第二个也是,因为如果你在解释器中运行mock(spec=os.path.exists)(在导入模拟之后),那将不会出现同样的错误。
  • 请注意,测试/模拟实际上与错误无关。但是我当时并不知道,因此提出了 SO 问题。此外,您看到的两个类实际上是同一个类,只是进行了一些修改以说明为什么我认为 os.path.exists 的行为不同。如果我只展示其中一个,它就不是 MWE,因为它不会说明 os.path.exists 的行为不同。
  • 不管怎样,感谢您的反馈,并感谢您留下评论来解释反对意见。

标签: python python-2.7 mocking python-unittest


【解决方案1】:

所以经过更多的研究和反复试验,大部分示例都在这里:http://www.voidspace.org.uk/python/mock/patch.html,我解决了我的问题。

import mock
import os

def side_effect(arg):
    if arg == 1:
        return True
    else:
        return False

class TestClass(unittest.TestCase):
    patcher = mock.patch('os.path.exists')
    mock_thing = patcher.start()
    mock_thing.side_effect = side_effect
    arg_list = [1, 2]
    ret = test(arg_list)
    self.assertItemsEqual([1], ret)

testarg_list 中的每个项目调用os.path.exist,并返回os.path.exist 为其返回True 的所有项目的列表。这个测试现在通过了我想要的。

【讨论】:

  • 如果函数在两个调用中具有相同的参数怎么办?
  • 什么意思?如果 os.path.exists 两个调用都传递了 1?然后它将返回 True 因为这就是副作用所说的。但它不会两次传递相同的参数,因为test 函数遍历arg_list 并为列表中的每个元素调用os.path.exists。所以只要列表中的两个元素都不是1,那么它就会按预期工作。
  • 我的意思是,如果我想用相同的参数调用 os.path.exists 两次,然后先返回 False,然后返回 True。我想出了如何定义一个函数来计算被调用的次数并返回不同的值并将其用作副作用。
【解决方案2】:

我相信你可以做到self.side_effect。由于初始定义不是全局的,因此调用 side_effect 会在全局范围内查找

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-24
    • 2015-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多