python - unittest - Mocking__init__ con parche mientras se conserva la parte sin parche



python unittest mock return value (1)

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

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




patch