2012-08-15 25 views
6

I tienen una llamada de función que devuelve un objeto:que imita las propiedades anidadas con mock

r = Foo(x,y) 

donde r tiene un rico conjunto de propiedades anidadas. Por ejemplo, puedo acceder al r.prop_a.prop_b.prop_c. Me gustaría burlarse Foo, de tal manera que una propiedad específica de las hojas de r se modifica, es decir, de tal manera que r.prop_a.prop_b.prop_c devuelve un valor bajo mi control:

>> r = Foo(x,y) 
>> r.prop_a.prop_b.prop_c 
'fish' 
>> # some mock magic patching of Foo is taking place here 
>> r = Foo(x,y) 
>> r.prop_a.prop_b.prop_c 
'my_fish' 

No me importa acerca de las propiedades intermedias mucho.

¿Existe alguna manera elegante de simular propiedades anidadas con mock?

+0

Puede muy bien ser que ciertas cosas se dejan como están. Terminó usando lo real en lugar de simulacro. – Oleksiy

Respuesta

10

Sustituir llam atributo del objeto de burla como era de esperar:

>> r1 = r_original(x, y) 
>> r1.prop_a.prop_b.prop_c 
'fish' 

>> returner = mock.MagicMock() 
>> returner.prop_a.prop_b.prop_c = 'fish' 
>> r_mocked = mock.MagicMock(spec_set=r_original, return_value=returner) 
>> r2 = r_mocked(x, y) 
>> r2.prop_a.prop_b 
MagicMock name='returner.prop_a.prop_b' id='87412560'> 
>> r2.prop_a.prop_b.prop_c 
'fish' 

Esto le permite a todo el poder de burla mientras se define un valor específico.

+0

¿Es r2 una instancia de la clase r_original? – Oleksiy

+0

No, es un objeto MagicMock. – dbn

3

Si desea exponer las propiedades originales en otro lugar, puede definir una clase contenedora:

class OverrideAttributePath(object): 
    """A proxy class where we override a specific attribute path with the 
    value given. For any other attribute path, we just return 
    attributes on the wrapped object. 

    """ 
    def __init__(self, thing, path, value): 
     self._thing = thing 
     self._path = path 
     self._value = value 

    def __dir__(self): 
     return dir(self._thing) 

    def __len__(self): 
     return len(self._thing) 

    def __getitem__(self, index): 
     if self._path == [index]: 
      return self._value 
     elif self._path[0] == index: 
      return OverrideAttributePath(
       self._thing[index], self._path[1:], self._value) 
     else: 
      return self._thing[index] 

    def __getattr__(self, key): 
     if self._path == [key]: 
      return self._value 
     elif self._path[0] == key: 
      return OverrideAttributePath(
       getattr(self._thing, key), self._path[1:], self._value) 
     else: 
      return getattr(self._thing, key) 

uso es entonces como sigue:

>>> r = Foo(x,y) 
>>> r2 = OverrideAttributePath(r, ['prop_a', 'prop_b', 'prop_c'], 'my_fish') 
>>> r2.prop_a.prop_b.prop_c 
'my_fish' 
Cuestiones relacionadas