【问题标题】:Mocking a function from builtin class从内置类模拟函数
【发布时间】:2020-11-29 19:27:37
【问题描述】:

是否可以从内置的list 类中模拟一个函数说append? 以下似乎不起作用

with patch("my_class.list.append", MagicMock()) as mock_dict:

我不想模拟整个类,只是一个函数。

我无法控制我正在测试的代码,所以我无法用我自己的代码来包装函数。

如果有人能指出正确的方向或一些文档,我将不胜感激。

【问题讨论】:

  • 有趣的案例,你能提供更多细节吗?您要模拟的最小部分,以及预期的结果是什么?
  • @Thymen 我正在为一群学生测试代码,他们不允许使用某些方法。所以我想如果我可以模拟这些函数,我可以跟踪这些函数是否被调用
  • 啊,对,如果他们使用从 MyList 类创建的变量,我的回答可以帮助你。所以不要给他们variable = list()variable = MyList()。或者,您可以编写一个正则表达式,将所有使用list() 的代码替换为MyList(),并在其文件的第一行插入MyList 导入。

标签: python python-3.x unit-testing mocking patch


【解决方案1】:

不能直接更改内置类,不会遇到很多麻烦,请查看this 帖子关于forbiddenfruit 存储库。

不过我们当然可以从内置继承,然后手动覆盖append方法。

如果没有更多细节,我不确定这个最小的工作示例是否能解决您的问题:

class MyList(list):
    def append(self, value):
        print('Mocked')


class Foo:
    var = list()
    var_filled = list([5, 6, 7, 8])


mock_foo_var = Foo.var
mock_foo_var = MyList(*mock_foo_var)
mock_foo_var.append(10)

mock_var_filled = Foo.var_filled
mock_var_filled = MyList(mock_var_filled)
mock_var_filled.append(10)

print(mock_foo_var)
print(mock_var_filled)

这将导致输出:

Mocked
Mocked
[]
[5, 6, 7, 8]

请注意,值 10 并未添加,而是用于打印 Mocked 输出。您可以随意更改此行为。

额外:

也许您确实想附加一个值,最简单的方法是使用 insertlen

def append(self, value):
    self.insert(len(self), value)
    print('Mocked')

如果这不是您要查找的内容,请更新问题并提供更多详细信息。


编辑 1

由于我们现在有了MyList 的新实例,我们还可以在模块级别更改list 引用。您不能更改list 的方法,但您可以重命名变量。所以以下也是可能的:

list = MyList

var = list()
var.append(10)

这将导致输出:

Mocked

如果此引用位于同一模块中(在调用 list 之前),则该引用将起作用。


编辑 2

想了一会儿,用例其实没那么简单。 python中创建列表的常用方法有3种:

  1. var = list()
  2. var = []
  3. var = list([])

上述方法仅适用于第一种和最后一种情况。

[] = list()
list = MyList

def test_case_1():
    var = list()
    var.append(10)
    print("Passed test 1:", len(var) == 0)


def test_case_2():
    var = list([])
    var.append(10)
    print("Passed test 2:", len(var) == 0)


def test_case_3():
    var = list([1, 2, 3])
    var.append(10)
    print("Passed test 3:", len(var) == 3)


def test_case_4():
    var = []
    var.append(10)
    print("Passed test 4:", len(var) == 0)


def test_case_5():
    var = [1, 2, 3]
    var.append(10)
    print("Passed test 5:", len(var) == 3)

test_case_1()
test_case_2()
test_case_3()
test_case_4()
test_case_5()

将导致输出:

Mocked
Passed test 1: True
Mocked
Passed test 2: True
Mocked
Passed test 3: True
Passed test 4: False
Passed test 5: False

测试用例 4 和 5 的 var 将包含:

  1. [10]
  2. [1, 2, 3, 10]

因此,当使用此解决方案时,所有列表都必须使用list() 显式创建,并且不能使用[ ] 列表分配。

如果有人知道如何覆盖 [ ] 函数,我很想知道如何。

【讨论】:

    【解决方案2】:

    我最近发布了一个名为fishhook 的python 库,它允许以与forbiddenfruit 略有不同的方式挂钩方法,使其更加稳定,同时还提供了一种调用原始实现的方法。

    对于list.append,您可以这样使用它

    from fishhook import hook, unhook, orig
    
    @hook(list)
    def append(self, item):
         print('list.append was called')
         return orig(self, item)
    

    要解开方法,调用unhook(list, 'append')

    【讨论】:

      猜你喜欢
      • 2017-02-04
      • 1970-01-01
      • 2014-08-24
      • 2020-09-09
      • 1970-01-01
      • 2018-12-18
      相关资源
      最近更新 更多