Chế giễu một lớp: Mock () hay patch ()?


116

Tôi đang sử dụng giả lập với Python và tự hỏi cách tiếp cận nào trong hai cách tiếp cận đó tốt hơn (đọc: thêm pythonic).

Phương pháp một : Chỉ cần tạo một đối tượng giả và sử dụng nó. Mã trông giống như:

def test_one (self):
    mock = Mock()
    mock.method.return_value = True 
    self.sut.something(mock) # This should called mock.method and checks the result. 
    self.assertTrue(mock.method.called)

Phương pháp hai : Sử dụng bản vá để tạo mô hình. Mã trông giống như:

@patch("MyClass")
def test_two (self, mock):
    instance = mock.return_value
    instance.method.return_value = True
    self.sut.something(instance) # This should called mock.method and checks the result. 
    self.assertTrue(instance.method.called)

Cả hai phương pháp đều làm điều tương tự. Tôi không chắc về sự khác biệt.

Bất cứ ai có thể khai sáng cho tôi?


10
Là một người chưa bao giờ thử Mock () hoặc bản vá, tôi cảm thấy rằng phiên bản đầu tiên rõ ràng hơn và hiển thị những gì bạn muốn làm, mặc dù tôi không hiểu sự khác biệt thực tế. Tôi không biết liệu điều này có giúp ích được gì hay không nhưng tôi nghĩ rằng nó có thể hữu ích để truyền đạt những gì một lập trình viên chưa bắt đầu có thể cảm thấy.
Michael Brennan

2
@MichaelBrennan: Cảm ơn bạn đã nhận xét. Nó thực sự hữu ích.
Sardathrion - chống lại sự lạm dụng SE.

Câu trả lời:


151

mock.patchlà một sinh vật rất khác với mock.Mock. patch thay thế lớp bằng một đối tượng giả và cho phép bạn làm việc với thể hiện giả. Hãy xem đoạn mã này:

>>> class MyClass(object):
...   def __init__(self):
...     print 'Created MyClass@{0}'.format(id(self))
... 
>>> def create_instance():
...   return MyClass()
... 
>>> x = create_instance()
Created MyClass@4299548304
>>> 
>>> @mock.patch('__main__.MyClass')
... def create_instance2(MyClass):
...   MyClass.return_value = 'foo'
...   return create_instance()
... 
>>> i = create_instance2()
>>> i
'foo'
>>> def create_instance():
...   print MyClass
...   return MyClass()
...
>>> create_instance2()
<mock.Mock object at 0x100505d90>
'foo'
>>> create_instance()
<class '__main__.MyClass'>
Created MyClass@4300234128
<__main__.MyClass object at 0x100505d90>

patchthay thế MyClasstheo cách cho phép bạn kiểm soát việc sử dụng lớp trong các hàm mà bạn gọi. Sau khi bạn vá một lớp, các tham chiếu đến lớp được thay thế hoàn toàn bằng thể hiện giả.

mock.patchthường được sử dụng khi bạn đang kiểm tra một thứ gì đó tạo ra một phiên bản mới của một lớp bên trong kiểm tra. mock.Mockcác trường hợp rõ ràng hơn và được ưu tiên hơn. Nếu self.sut.somethingphương thức của bạn tạo một thể hiện MyClassthay vì nhận một thể hiện dưới dạng một tham số, thì mock.patchở đây sẽ phù hợp.


2
@ D.Shawley làm cách nào để chúng ta vá một lớp được khởi tạo bên trong một lớp khác cần được thử nghiệm.
ravi404

4
@ravz - đọc "Nơi để vá" . Đây là một trong những điều khó khăn hơn để làm việc hiệu quả.
D.Shawley

Bài thi thử của tôi tương tự như Phương pháp hai . Tôi muốn cá thể MyClass đưa ra một ngoại lệ. Tôi đã thử cả mock.side_effect và mock.return_value.side_effect và những thứ đó không hoạt động. Tôi làm gì?
Hussain

5
@ D.Shawley Liên kết bị hỏng, bạn có thể tìm thấy nó ở đây ngay bây giờ: "Nơi để vá"
RazerM

2
Để vá một đối tượng lớp, hãy xem stackoverflow.com/questions/8469680/…
bão_m2138

27

Tôi có một video YouTube về điều này.

Câu trả lời ngắn gọn: Sử dụng mockkhi bạn đang đi qua thứ mà bạn muốn chế nhạo, và patchnếu bạn không. Trong số hai, mô hình giả được ưa thích hơn hẳn vì nó có nghĩa là bạn đang viết mã bằng cách tiêm phụ thuộc thích hợp.

Ví dụ ngớ ngẩn:

# Use a mock to test this.
my_custom_tweeter(twitter_api, sentence):
    sentence.replace('cks','x')   # We're cool and hip.
    twitter_api.send(sentence)

# Use a patch to mock out twitter_api. You have to patch the Twitter() module/class 
# and have it return a mock. Much uglier, but sometimes necessary.
my_badly_written_tweeter(sentence):
    twitter_api = Twitter(user="XXX", password="YYY")
    sentence.replace('cks','x') 
    twitter_api.send(sentence)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.