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()