Python Mock对象中方法被调用多次

80 投票
5 回答
63679 浏览
提问于 2025-04-17 03:44

我有一个正在测试的类,它依赖于另一个类(这个类的实例会被传递到被测类的初始化方法中)。我想用Python的Mock库来模拟这个依赖的类。

我现在的代码大概是这样的:

mockobj = Mock(spec=MyDependencyClass)
mockobj.methodfromdepclass.return_value = "the value I want the mock to return"
assertTrue(mockobj.methodfromdepclass(42), "the value I want the mock to return")

cutobj = ClassUnderTest(mockobj)

这样做是可以的,但“methodfromdepclass”是一个带参数的方法,所以我想创建一个单一的模拟对象,根据传给“methodfromdepclass”的不同参数返回不同的值。

我想要这种带参数的行为是因为我想创建多个被测类的实例,每个实例包含不同的值(这些值是通过模拟对象返回的结果生成的)。

我在想的方式(当然这并不奏效)是:

mockobj = Mock(spec=MyDependencyClass)
mockobj.methodfromdepclass.ifcalledwith(42).return_value = "you called me with arg 42"
mockobj.methodfromdepclass.ifcalledwith(99).return_value = "you called me with arg 99"

assertTrue(mockobj.methodfromdepclass(42), "you called me with arg 42")
assertTrue(mockobj.methodfromdepclass(99), "you called me with arg 99")

cutinst1 = ClassUnderTest(mockobj, 42)
cutinst2 = ClassUnderTest(mockobj, 99)

# now cutinst1 & cutinst2 contain different values

我该如何实现这种“如果调用时带有某些参数”的效果呢?

5 个回答

11

我在自己测试的时候遇到了这个问题。如果你不在乎记录对你方法 methodfromdepclass() 的调用,只是想让它返回一些东西,那么下面的代码就可以满足你的需求:

def makeFakeMethod(mapping={}):
    def fakeMethod(inputParam):
        return mapping[inputParam] if inputParam in mapping else MagicMock()
    return fakeMethod

mapping = {42:"Called with 42", 59:"Called with 59"}
mockobj.methodfromdepclass = makeFakeMethod(mapping)

这里有一个带参数的版本:

def makeFakeMethod():
    def fakeMethod(param):
        return "Called with " + str(param)
    return fakeMethod
71

稍微简单一点:

mockobj.method.side_effect = lambda x: {123: 100, 234: 10000}[x]

或者可以处理多个参数:

mockobj.method.side_effect = lambda *x: {(123, 234): 100, (234, 345): 10000}[x]

或者可以设置一个默认值:

mockobj.method.side_effect = lambda x: {123: 100, 234: 10000}.get(x, 20000)

或者两者结合起来:

mockobj.method.side_effect = lambda *x: {(123, 234): 100, (234, 345): 10000}.get(x, 20000)

然后我们就可以愉快地继续了。

113

试试 side_effect 这个东西

def my_side_effect(*args, **kwargs):
    if args[0] == 42:
        return "Called with 42"
    elif args[0] == 43:
        return "Called with 43"
    elif kwargs['foo'] == 7:
        return "Foo is seven"

mockobj.mockmethod.side_effect = my_side_effect

撰写回答