2012-08-29 33 views
84

Tôi đang sử dụng thư viện Mock để kiểm tra ứng dụng của mình, nhưng tôi muốn khẳng định rằng một số chức năng không được gọi. Tài liệu giả lập nói về các phương pháp như mock.assert_called_withmock.assert_called_once_with, nhưng tôi không tìm thấy bất kỳ thứ gì như mock.assert_not_called hoặc một thứ liên quan đến xác minh mô hình là KHÔNG được gọi là.Xác nhận một hàm/phương thức không được gọi bằng cách sử dụng Mock

tôi có thể đi với một cái gì đó như sau, mặc dù nó không có vẻ mát mẻ và cũng không pythonic:

def test_something: 
    # some actions 
    with patch('something') as my_var: 
     try: 
      # args are not important. func should never be called in this test 
      my_var.assert_called_with(some, args) 
     except AssertionError: 
      pass # this error being raised means it's ok 
    # other stuff 

Bất kỳ ý tưởng làm thế nào để thực hiện điều này?

Cảm ơn bạn đã trợ giúp :)

+0

Như @Ahmet chỉ ra trong câu trả lời của mình, assert_not_called hiện đang được hỗ trợ, cũng trong backport (https://docs.python.org/3/library/unittest.mock.html#unittest.mock .Mock.assert_not_called). – Martin

Trả lời

106

Điều này sẽ hữu ích cho trường hợp của bạn;

assert not my_var.called, 'method should not have been called' 

Mẫu;

>>> mock=Mock() 
>>> mock.a() 
<Mock name='mock.a()' id='4349129872'> 
>>> assert not mock.b.called, 'b was called and should not have been' 
>>> assert not mock.a.called, 'a was called and should not have been' 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AssertionError: a was called and should not have been 
+0

Câu trả lời này có yêu cầu Django không? Tôi nhận được một lỗi: 'AttributeError: MockCallable dụ không có thuộc tính 'được gọi là' ' –

+0

@NathanArthur Hm, tôi không nghĩ vậy, sau' sudo easy_install -U mock' và 'từ mock import Mock' trên MacOS, ở trên chạy mà không có một xô. Chưa bao giờ cài đặt Django :) –

+0

Hmm. Thật ki quặc. Tôi đang chạy Python 2.7.1 và đang sử dụng unittest và 'từ mock import Mock' với Python Mock 0.1.0 cho các bài kiểm tra của tôi. Có bất kỳ âm thanh nào có vấn đề? –

25

Bạn có thể kiểm tra các thuộc tính called, nhưng nếu khẳng định của bạn thất bại, điều tiếp theo bạn sẽ muốn biết là cái gì đó về cuộc gọi bất ngờ, vì vậy bạn cũng có thể sắp xếp để biết thông tin đó sẽ được hiển thị từ lúc bắt đầu. Sử dụng unittest, bạn có thể kiểm tra các nội dung của call_args_list thay vì:

self.assertItemsEqual(my_var.call_args_list, []) 

Khi nó không thành công, nó mang lại một thông điệp như thế này:

 
AssertionError: Element counts were not equal: 
First has 0, Second has 1: call('first argument', 4) 
11

Khi bạn kiểm tra sử dụng lớp kế thừa unittest.TestCase bạn có thể chỉ cần sử dụng các phương pháp như:

  • assertTrue
  • assertFalse
  • assertEqual

và tương tự (trong python documentation bạn tìm thấy phần còn lại).

Trong ví dụ của bạn, chúng tôi chỉ có thể khẳng định nếu mock_method.called tài sản là False, có nghĩa là phương pháp không được gọi.

import unittest 
from unittest import mock 

import my_module 

class A(unittest.TestCase): 
    def setUp(self): 
     self.message = "Method should not be called. Called {times} times!" 

    @mock.patch("my_module.method_to_mock") 
    def test(self, mock_method): 
     my_module.method_to_mock() 

     self.assertFalse(mock_method.called, 
         self.message.format(times=mock_method.call_count)) 
0

Đánh giá từ các câu trả lời khác, không ai ngoại trừ @rob-kennedy nói về call_args_list.

Đó là một công cụ mạnh mẽ cho rằng bạn có thể thực hiện ngược lại chính xác của MagicMock.assert_called_with()

call_args_list là danh sách các đối tượng call. Mỗi đối tượng call đại diện cho một cuộc gọi được thực hiện trên một cuộc gọi có thể gọi được.

>>> from unittest.mock import MagicMock 
>>> m = MagicMock() 
>>> m.call_args_list 
[] 
>>> m(42) 
<MagicMock name='mock()' id='139675158423872'> 
>>> m.call_args_list 
[call(42)] 
>>> m(42, 30) 
<MagicMock name='mock()' id='139675158423872'> 
>>> m.call_args_list 
[call(42), call(42, 30)] 

Tiêu thụ một đối tượng call rất dễ dàng, vì bạn có thể so sánh nó với một tuple có độ dài 2 nơi các thành phần đầu tiên là một tuple chứa tất cả các đối số vị trí của cuộc gọi liên quan, trong khi phần thứ hai là một cuốn từ điển của các đối số từ khóa.

>>> ((42,),) in m.call_args_list 
True 
>>> m(42, foo='bar') 
<MagicMock name='mock()' id='139675158423872'> 
>>> ((42,), {'foo': 'bar'}) in m.call_args_list 
True 
>>> m(foo='bar') 
<MagicMock name='mock()' id='139675158423872'> 
>>> ((), {'foo': 'bar'}) in m.call_args_list 
True 

Vì vậy, một cách để giải quyết vấn đề cụ thể của OP là

def test_something(): 
    with patch('something') as my_var: 
     assert ((some, args),) not in my_var.call_args_list 

Lưu ý rằng cách này, thay vì chỉ kiểm tra nếu một chế giễu callable đã được gọi là, thông qua MagicMock.called, bạn có thể bây giờ kiểm tra xem nó đã được gọi với một bộ đối số cụ thể chưa.

Điều đó hữu ích. Giả sử bạn muốn kiểm tra hàm nhận danh sách và gọi hàm khác, compute(), cho mỗi giá trị của danh sách chỉ khi chúng đáp ứng một điều kiện cụ thể.

Bây giờ bạn có thể giả lập compute và kiểm tra xem nó đã được gọi trên một số giá trị nhưng không được đặt trên một số giá trị khác.

26

Mặc dù câu hỏi cũ, tôi muốn thêm rằng thư viện hiện tại mock (cổng sau của unittest.mock) hỗ trợ phương thức assert_not_called.

Chỉ cần nâng cấp máy của bạn;

pip install mock --upgrade

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