2012-08-31 38 views
12

Tôi cố gắng sử dụng mock để viết một số đơn vị thử nghiệm trong python.Làm thế nào để giả lập một lớp cơ sở với thư viện giả python

Ví dụ tôi có lớp sau:

class TCPHandler(socketserver.BaseRequestHandler): 
    def handle(self): 
     self.data = self.request.recv(1024).strip() 

Và tôi chỉ muốn thử nghiệm phương pháp handle. Mà không cần phải giả định bất cứ điều gì về socketserver.BaseRequestHandler. Ví dụ: tôi muốn xác nhận rằng handle gọi recv với đối số 1024. Có thể làm điều đó với giả? I E. thay thế lớp cơ sở socketserver.BaseRequestHandler bằng mô hình? Hay tôi theo dõi ý tưởng đó?


Với câu trả lời của ecatmur (! Cảm ơn bạn) lần đầu tiên tôi thử như sau:

patcher = patch.object(TCPHandler, '__bases__', (Mock,)) 
with patcher: 
    patcher.is_local = True 
    handler = TCPHandler() 
    handler.handle() 

Nhưng bây giờ handle không được gọi anylonger và dir(handler) cho:

['assert_any_call', 'assert_called_once_with', 'assert_called_with', 'assert_has_calls', 'attach_mock', 'call_args', 'call_args_list', 'call_count', 'called', 'configure_mock', 'method_calls', 'mock_add_spec', 'mock_calls', 'reset_mock', 'return_value', 'side_effect'] 

type(handler) cho <class 'mock.TCPHandler'>

Tôi giải thích rằng việc vá lớp cơ sở cũng biến lớp dẫn xuất của tôi thành một mô hình.


bây giờ tôi đã ý tưởng khác thử xem:

mock = MagicMock() 
TCPHandler.handle(mock) 
#assertions 

Tuy nhiên, mô hình có vẻ như không được gọi.

Trả lời

17

Bạn có thể làm điều này bằng cách vá lớp được thừa kế của __bases__:

def test_derived(): 
    patcher = mock.patch.object(Derived, '__bases__', (mock.Mock,)) 
    with patcher: 
     patcher.is_local = True 
     d = Derived() 
     print d.foo() 

Các is_local hack là cần thiết để ngăn chặn mock.patch từ cố gắng để gọi delattr khi đảo ngược các bản vá.

+0

Tôi đã làm theo gợi ý của bạn nhưng hậu quả là, đó cũng là phương pháp của lớp dẫn xuất được thay thế bằng mô hình. –

+0

Tôi đặt mã thử nghiệm hiện tại của mình trong câu hỏi, nếu bạn có thể có giao diện? Tôi đánh giá cao sự giúp đỡ của bạn. –

+0

@FrederickRoth không quá chắc chắn; nó hoạt động tốt cho tôi với Python 2. Có lẽ thử chế nhạo các phần của đối tượng mà phương thức truy cập? – ecatmur

4

Tôi nghĩ rằng vấn đề thực sự là bạn đang cố gắng giả lập mã thực tế mà bạn muốn thử nghiệm. Thay vì các đối tượng đang được gọi bởi mã đó. Nếu bạn quan tâm đến việc xem liệu phương thức xử lý có gọi phương thức recv trên self.request hay không thì hãy thử phương thức recv.

def test_tcp_handler_method(self): 

    handler = TCPHandler() 
    handler.request = Mock() 

    handler.handle() 

    self.assertTrue(handler.request.recv.called) 
    self.assertEqual(handler.request.recv.call_args[0], 1024) 

Bạn có thể phải thực hiện thêm một số thiết lập để xử lý để khởi tạo nhưng ý tưởng cơ bản phải rõ ràng.

+0

Cảm ơn bạn đã nhập! TCPHandler cần một tcprequest để khởi tạo và do đó không bao giờ được instantiated trực tiếp bởi mã của tôi, mà bởi TCPServer. Đây là toàn bộ lý do tại sao tôi không muốn gọi nó là '__init__'. Tôi chỉ muốn thử nghiệm phương pháp nhỏ của riêng mình mà không cần kiểm tra toàn bộ khung công tác tcpserver. –

4

Tôi không biết đó có phải là giải pháp tốt nhất hay không nhưng tôi đã xác định lại lớp trước đó với một phụ huynh khác bằng cách sử dụng type().Tôi đã xây dựng một chức năng gọi là patch_parent(), mà trả về lớp với một mô hình mẹ:

from contextlib import contextmanager 

@contextmanager 
def patch_parent(class_): 
    """ 
    Mock the bases 
    """ 
    yield type(class_.__name__, (Mock,), dict(class_.__dict__)) 

Sau đó, bạn có thể sử dụng patch_parent như thế này:

class Bar(): 
    def method(self, param1, param2...): 
     ... 

class Foo(Bar): 
    pass 


>>> with patch_parent(Foo) as MockFoo: 
...  f = MockFoo() 
...  print f 
...  print f.method() 
... 
<Foo id='15488016'> 
<Foo name='mock.method()' id='15541520'> 
>>> s = Foo() 
>>> s.method() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: method() takes exactly 3 arguments (1 given) 

Lớp MockFoo vẫn có các phương pháp của Foo lớp và không có phương thức được xác định trong cấp độ gốc bởi vì cấp độ gốc hiện là lớp Mock.

Các vấn đề liên quan