unittest unit test mock python unit-testing mocking python-mock

mock - python unittest



Objeto Python Mock con método llamado varias veces (3)

Me encontré con esto cuando estaba haciendo mi propia prueba. Si no le interesa capturar llamadas a su método dedepclass () pero solo necesita devolver algo, entonces lo siguiente puede ser suficiente:

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)

Aquí hay una versión parametrizada:

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

Tengo una clase que estoy probando que tiene como dependencia otra clase (una instancia de la cual pasa al método init de la CUT). Quiero simular esta clase usando la biblioteca Python Mock.

Lo que tengo es algo así como:

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)

Lo cual está bien, pero "methodfromdepclass" es un método parametrizado, y como tal quiero crear un solo objeto simulado donde, dependiendo de qué argumentos se pasan a methodfromdepclass, devuelve valores diferentes.

La razón por la que deseo este comportamiento parametrizado es que quiero crear varias instancias de ClassUnderTest que contengan valores diferentes (cuyos valores son producidos por lo que devuelve el mockobj).

Algo de lo que estoy pensando (esto por supuesto no funciona):

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

¿Cómo logro este tipo de semántica "ifcalledwith"?


Prueba 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 kwarg[''foo''] == 7: return "Foo is seven" mockobj.mockmethod.side_effect = my_side_effect


Un poco más dulce:

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

o para múltiples argumentos:

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

o con un valor predeterminado:

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

O una combinación de ambos:

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

y alegremente en lo alto vamos.