从内置函数模拟函数 class

Mocking a function from builtin class

是否可以从内置 list class 模拟一个函数 say append? 以下似乎不起作用

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

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

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

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

直接更改内置 class 是不可能的,如果不麻烦的话,请检查 this post 关于 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() 显式创建,并且不能使用 [ ] 列表分配。

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

我最近发布了一个名为 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')