unittest unit test mock python unit-testing mocking patch

mock - python unittest



Mocking__init__ con parche mientras se conserva la parte sin parche (1)

Aquí la solución:

import unittest from mock import patch class PortfolioConfig(object): ini_value = 10 def __init__(self, maxtrades, name): self.maxtrades = maxtrades + 2 self.name = name class MREUnitTestBase(unittest.TestCase): @staticmethod def change_wrapper(class_to_wrap, override_constructor_properties=None, override_class_properties=None): method_to_wrap = class_to_wrap.__init__ def wrapped(self, *args, **kwargs): for k, v in override_constructor_properties.items(): kwargs[k] = v return method_to_wrap(self, *args, **kwargs) for k, v in override_class_properties.items(): setattr(class_to_wrap, k, v) return wrapped class PortfoloConfigTest(MREUnitTestBase): portfolio_override = {''maxtrades'': 40} portfolio_override_properties = {''ini_value'': 100} @patch.object(PortfolioConfig, ''__init__'', MREUnitTestBase.change_wrapper(PortfolioConfig, portfolio_override, portfolio_override_properties)) def test_change_in_portfolio_config_by_test(self): p = program_run() # running main program self.assertEqual(p.maxtrades, 42) # was overridden by test self.assertEqual(p.name, ''hello'') self.assertEqual(p.ini_value, 100) # this is the program code def program_run(): p = PortfolioConfig(maxtrades=100, name=''hello'') return p

Esta pregunta ya tiene una respuesta aquí:

¿Cómo puedo __init__ una función __init__ de una clase con @patch desde el módulo Mocking en python de la siguiente manera:

Esto es lo que intenté:

import mock from mock import patch class ConfigPortfolio(object): name = None def __init__(self, name=None, number=None): self.name = name self.number = number with patch.object(ConfigPortfolio, ''name'') as mock_name: mock_name.__get__ = mock.Mock(return_value=''mocked name'') # here I will call some functions in the actual prograam, where ConfigPortfolio will be instantied and I want to makre sure name will stay patched. p = ConfigPortfolio(name=''hello'', number=3) # will be deeply hidden in a function. print p.name # this should have changed to mocked name print p.number # this needs to stay 3 as it is instantiated with, as it is not overwritten by the wrapper.

El resultado es

hello 3

El objeto patch.object no cambia el nombre, porque __init__ reinicia la propiedad de la clase que está parcheada antes de llamar a init. ¿Cómo puedo anular solo una propiedad específica, que está establecida en __init__ ?

La dificultad en este caso es que es una clase donde el __init__ necesita ser envuelto y puede ser anulado con cualquier propiedad. Cuando se crea una instancia de la clase en la prueba real (la instanciación ocurrirá en el fondo del código en alguna parte, donde no tengo control sobre ella), no importará con qué valor la instaure. Es el valor que establezco en el parche que anulará el valor de instauración del código.

actualizar:

He creado un nuevo ejemplo que envuelve la función init y debe anular su valor. En la actualidad, sin embargo, no hace eso. Cualquier sugerencia muestra para arreglarlo son apreciados.

import unittest from mock import MagicMock, patch # this is the main code over which I have no control def mre_run(): p = PortfolioConfig(n=100) return p.value # the overriding doesn''t seem to work yet def spy_decorator(method_to_decorate): mock = MagicMock() def wrapper(self, *args, **kwargs): mock(*args, **kwargs) return method_to_decorate(self, *args, **kwargs) wrapper.mock = mock return wrapper class PortfolioConfig(object): def __init__(self, n): self.value = n+2 class PotatoTest(unittest.TestCase): def test_something(self): wrp = spy_decorator(PortfolioConfig.__init__) with patch.object(PortfolioConfig, ''__init__'', wrp): result = mre_run() # somehow call it with n=40, the wrapper around PortfolioConfig should change the value. # foo.mock.assert_called_once_with(n=40) self.assertEqual(result, 42) if __name__ == ''__main__'': unittest.main()