Burlarse de un método de clase y cambiar algunos atributos de objeto en Python

Soy nuevo para burlarse de Python. Quiero saber cómo reemplazar (simular) un método de clase mientras se prueba con otro, sabiendo que el original simplemente cambia algunos atributos de sí mismo sin devolver ningún valor. Por ejemplo:

def some_method(self): self.x = 4 self.y = 6 

Así que aquí no puedo simplemente cambiar el valor de retorno de la simulación. Intenté definir una nueva función (que debería reemplazar la original) y darla como efecto secundario al simulacro. Pero, ¿cómo puedo hacer que la función de burla cambie los atributos del objeto en la clase? Aquí está mi código:

 @patch('path.myClass.some_method') def test_this(self,someMethod): def replacer(self): self.x = 5 self.y = 16 some_method.side_effect = replacer 

Entonces, ¿cómo Python ahora entiende el argumento del sustituto? ¿Es ese el yo de la clase de prueba, o el yo como el objeto de la clase bajo prueba?

Le pido disculpas por adelantado si no entiendo lo que está tratando de hacer, pero creo que esto podría funcionar:

 import unittest from unittest.mock import patch class MyClass: def __init__(self): self.x = 0 self.y = 0 def some_method(self): self.x = 4 self.y = 6 class OtherClass: def other_method(self): self.x = 5 self.y = 16 class MyTestClass(unittest.TestCase): @patch('__main__.MyClass.some_method', new=OtherClass.other_method) def test_patched(self): a = MyClass() a.some_method() self.assertEqual(ax, 5) self.assertEqual(ay, 16) def test_not_patched(self): a = MyClass() a.some_method() self.assertEqual(ax, 4) self.assertEqual(ay, 6) if __name__ == "__main__": unittest.main() 

Esto reemplaza some_method () con other_method () cuando está parcheado, lo que establece diferentes valores para los atributos x, y, y cuando se ejecuta la prueba, da los resultados:

 .. ---------------------------------------------------------------------- Ran 2 tests in 0.020s OK 

EDITAR: para responder una pregunta sobre cómo hacer dentro de la función de prueba sin burlarse de una clase …

 def test_inside_patch(self): def othermethod(self): self.x = 5 self.y = 16 patcher = patch('__main__.MyClass.some_method', new=othermethod) patcher.start() a = MyClass() a.some_method() self.assertEqual(ax, 5) self.assertEqual(ay, 16) patcher.stop() 

Asegúrese de llamar a start () y stop () en el parcheador, de lo contrario puede entrar en una situación en la que el parche esté activo y no quiera que esté. Tenga en cuenta que para definir la función simulada dentro de la función de código de prueba, no utilicé el parche como decorador, ya que la función simulada debe definirse antes de usar la palabra clave ‘nuevo’ en el parche. Si desea usar el parche como decorador, debe definir la función simulada en algún lugar antes del parche, también lo hace definir dentro de MyTestClass, pero parece que realmente desea tener la función simulada definida dentro del código de la función de prueba.

EDIT: agregado resumen de 4 formas que veo para hacer esto …

 # first way uses a class outside MyTest class class OtherClass: def other_method(self): ... class MyTest(unittest.TestCase): @patch('path_to_MyClass.some_method', new=OtherClass.other_method) def test_1(self) ... # 2nd way uses class defined inside test class class MyOtherClass: def other_method(self): ... @patch('path_to_MyClass.some_method', new=MyOtherClass.other_method) def test_2(self): ... # 3rd way uses function defined inside test class but before patch decorator def another_method(self): ... @patch('path_to_MyClass.some_method', new=another_method) def test_3(self): ... # 4th way uses function defined inside test function but without a decorator def test_4(self): def yet_another_method(self): ... patcher = patch('path_to_MyClass.some_method', new=yet_another_method) patcher.start() ... patcher.stop() 

Ninguno de estos utiliza un efecto secundario, pero todos resuelven el problema de burlarse de un método de clase y cambiar algunos atributos. El que elija dependerá de la aplicación.