2012-08-15 40 views
6

Tôi có một cuộc gọi hàm trả về một đối tượng:Mocking tính lồng nhau với mock

r = Foo(x,y) 

nơi r có một tập hợp phong phú các thuộc tính lồng nhau. Ví dụ: tôi có thể truy cập r.prop_a.prop_b.prop_c. Tôi muốn thử Foo, như vậy mà một tài sản cụ thể của lá r được sửa đổi, ví dụ như vậy r.prop_a.prop_b.prop_c trả về một giá trị dưới sự kiểm soát của tôi:

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

Tôi không quan tâm về các tính chất trung gian nhiều.

Có cách nào thanh lịch để giả lập các thuộc tính lồng nhau với mock không?

+0

Nó có thể rất tốt được rằng những thứ nhất định còn lại theo cách của họ. Kết thúc bằng cách sử dụng thực tế thay vì giả. – Oleksiy

Trả lời

10

Thay thế cuộc gọi thuộc tính đối tượng giả như bạn mong chờ:

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

này cho phép bạn toàn bộ sức mạnh của chế giễu trong khi xác định một giá trị cụ thể.

+0

R2 có phải là một thể hiện của lớp r_original không? – Oleksiy

+0

Không, nó là một đối tượng MagicMock. – dbn

3

Nếu bạn muốn để lộ các thuộc tính ban đầu ở nơi khác, bạn có thể định nghĩa một lớp wrapper:

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) 

Cách sử dụng sau đó như sau:

>>> r = Foo(x,y) 
>>> r2 = OverrideAttributePath(r, ['prop_a', 'prop_b', 'prop_c'], 'my_fish') 
>>> r2.prop_a.prop_b.prop_c 
'my_fish' 
Các vấn đề liên quan