2012-08-15 11 views
6

Mam wywołanie funkcji, która zwraca obiekt:szyderczy właściwości zagnieżdżonych z udawanym

r = Foo(x,y) 

gdzie r posiada bogaty zestaw właściwości zagnieżdżonych. Na przykład mogę uzyskać dostęp do r.prop_a.prop_b.prop_c. Chciałbym drwić Foo tak, że konkretna nieruchomość liść r zostanie zmodyfikowany, to znaczy takie, które r.prop_a.prop_b.prop_c zwraca wartość pod moją kontrolą:

>> 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' 

Nie dbam o właściwościach pośrednich dużo.

Czy istnieje elegancki sposób na wyśmiewanie zagnieżdżonych nieruchomości za pomocą mock?

+0

To może być bardzo dobrze, że pewne rzeczy są pozostawione tak jak oni są. Skończyło się na używaniu prawdziwej rzeczy zamiast udawać. – Oleksiy

Odpowiedz

10

Wymienić połączenia przypisują atrapa obiektu jest jak można oczekiwać:

>> 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' 

Pozwala to pełną moc szydząc przy definiowaniu określonej wartości.

+0

Czy r2 jest instancją klasy r_original? – Oleksiy

+0

Nie, to obiekt MagicMock. – dbn

3

Jeśli chcesz wystawiać oryginalne właściwości gdzie indziej, można zdefiniować klasy otoki:

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) 

Wykorzystanie wygląda następująco:

>>> r = Foo(x,y) 
>>> r2 = OverrideAttributePath(r, ['prop_a', 'prop_b', 'prop_c'], 'my_fish') 
>>> r2.prop_a.prop_b.prop_c 
'my_fish' 
Powiązane problemy