使用多次调用方法的Python Mock对象

Ada*_*kin 48 python unit-testing mocking python-mock

我有一个我正在测试的类,它具有另一个类的依赖(其实例被传递给CUT的init方法).我想使用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)
Run Code Online (Sandbox Code Playgroud)

这很好,但是"methodfromdepclass"是一个参数化方法,因此我想创建一个模拟对象,根据传递给methodfromdepclass的参数,它返回不同的值.

我想要这个参数化行为的原因是我想创建包含不同值的ClassUnderTest的多个实例(其值由mockobj返回的值生成).

有点我在想什么(这当然不起作用):

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
Run Code Online (Sandbox Code Playgroud)

我如何实现这种"ifcalledwith"类型的语义?

k.p*_*ell 75

尝试 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
Run Code Online (Sandbox Code Playgroud)

  • 太棒了,正是我需要的.不是语法的粉丝,但工作真棒.谢谢! (4认同)
  • 我想在这里添加的一个警告是我在使用这个解决方案时运行的,顺便说一句,效果很好,顺便说一句,如果您打算将异常作为副作用,则必须引发它们,而不是返回它们。Mock 库很适合让你为 side_effect 分配一个异常并弄清楚它,但使用这种方法你必须自己动手做。 (2认同)

abo*_*get 50

有点甜:

mockobj.method.side_effect = lambda x: {123: 100, 234: 10000}[x]
Run Code Online (Sandbox Code Playgroud)

或多个参数:

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

或者使用默认值:

mockobj.method.side_effect = lambda x: {123: 100, 234: 10000}.get(x, 20000)
Run Code Online (Sandbox Code Playgroud)

或两者兼而有之:

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

我们高兴得高兴.


Add*_*son 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)
Run Code Online (Sandbox Code Playgroud)

这是一个参数化版本:

def makeFakeMethod():
    def fakeMethod(param):
        return "Called with " + str(param)
    return fakeMethod
Run Code Online (Sandbox Code Playgroud)