unittest.mock --- thư viện đối tượng giả

Added in version 3.3.

Source code: Lib/unittest/mock.py


unittest.mock là một thư viện để thử nghiệm bằng Python. Nó cho phép bạn thay thế các phần của hệ thống đang được thử nghiệm bằng các đối tượng mô phỏng và đưa ra các xác nhận về cách chúng được sử dụng.

unittest.mock cung cấp một lớp Mock cốt lõi, loại bỏ nhu cầu tạo một loạt các bài sơ khai trong bộ thử nghiệm của bạn. Sau khi thực hiện một hành động, bạn có thể đưa ra xác nhận về phương thức/thuộc tính nào đã được sử dụng và các đối số mà chúng được gọi. Bạn cũng có thể chỉ định giá trị trả về và đặt các thuộc tính cần thiết theo cách thông thường.

Ngoài ra, mô hình còn cung cấp một trình trang trí patch() xử lý các thuộc tính cấp độ lớp và mô-đun vá lỗi trong phạm vi thử nghiệm, cùng với sentinel để tạo các đối tượng duy nhất. Xem quick guide để biết một số ví dụ về cách sử dụng Mock, MagicMockpatch().

Mock được thiết kế để sử dụng với unittest và dựa trên mẫu 'hành động -> xác nhận' thay vì 'bản ghi -> phát lại' được nhiều khung mô phỏng sử dụng.

Có một backport của unittest.mock cho các phiên bản Python trước đó, có sẵn dưới dạng mock trên PyPI.

Hướng dẫn nhanh

Các đối tượng MockMagicMock tạo tất cả các thuộc tính và phương thức khi bạn truy cập chúng và lưu trữ thông tin chi tiết về cách chúng được sử dụng. Bạn có thể định cấu hình chúng, để chỉ định giá trị trả về hoặc giới hạn những thuộc tính có sẵn, sau đó đưa ra xác nhận về cách chúng được sử dụng:

>>> from unittest.mock import MagicMock
>>> thing = ProductionClass()
>>> thing.method = MagicMock(return_value=3)
>>> thing.method(3, 4, 5, key='value')
3
>>> thing.method.assert_called_with(3, 4, 5, key='value')

side_effect cho phép bạn thực hiện các tác dụng phụ, bao gồm đưa ra một ngoại lệ khi một bản mô phỏng được gọi:

>>> from unittest.mock import Mock
>>> mock = Mock(side_effect=KeyError('foo'))
>>> mock()
Traceback (most recent call last):
 ...
KeyError: 'foo'
>>> values = {'a': 1, 'b': 2, 'c': 3}
>>> def side_effect(arg):
...     return values[arg]
...
>>> mock.side_effect = side_effect
>>> mock('a'), mock('b'), mock('c')
(1, 2, 3)
>>> mock.side_effect = [5, 4, 3, 2, 1]
>>> mock(), mock(), mock()
(5, 4, 3)

Mock có nhiều cách khác để bạn có thể định cấu hình và kiểm soát hành vi của nó. Ví dụ: đối số spec định cấu hình mô hình để lấy thông số kỹ thuật của nó từ một đối tượng khác. Việc cố gắng truy cập các thuộc tính hoặc phương thức trên mô hình không tồn tại trên thông số kỹ thuật sẽ không thành công với AttributeError.

Trình quản lý bối cảnh/trang trí patch() giúp dễ dàng mô phỏng các lớp hoặc đối tượng trong mô-đun đang được thử nghiệm. Đối tượng bạn chỉ định sẽ được thay thế bằng một mô hình (hoặc đối tượng khác) trong quá trình thử nghiệm và được khôi phục khi quá trình thử nghiệm kết thúc:

>>> từ bản  nhập unittest.mock
>>> @patch('module.ClassName2')
... @patch('module.ClassName1')
... kiểm tra độ chính xác (MockClass1, MockClass2):
... -đun.ClassName1()
... -đun.ClassName2()
... khẳng định MockClass1  module.ClassName1
... khẳng định MockClass2  module.ClassName2
... khẳng định MockClass1.gọi
... khẳng định MockClass2.gọi
...
>>> kiểm tra()

Ghi chú

Khi bạn lồng các trình trang trí bản vá, các mô phỏng sẽ được chuyển vào hàm trang trí theo đúng thứ tự mà chúng đã áp dụng (thứ tự Python thông thường mà các trình trang trí được áp dụng). Điều này có nghĩa là từ dưới lên, vì vậy trong ví dụ trên, mô hình cho module.ClassName1 được chuyển vào đầu tiên.

Với patch(), điều quan trọng là bạn vá các đối tượng trong không gian tên nơi chúng được tra cứu. Điều này thường đơn giản nhưng để có hướng dẫn nhanh, hãy đọc where to patch.

Ngoài ra, trình trang trí patch() có thể được sử dụng làm trình quản lý bối cảnh trong câu lệnh with:

>>> with patch.object(ProductionClass, 'method', return_value=None) as mock_method:
...     thing = ProductionClass()
...     thing.method(1, 2, 3)
...
>>> mock_method.assert_called_once_with(1, 2, 3)

Ngoài ra còn có patch.dict() để đặt giá trị trong từ điển chỉ trong phạm vi và khôi phục từ điển về trạng thái ban đầu khi quá trình kiểm tra kết thúc:

>>> foo = {'key': 'value'}
>>> original = foo.copy()
>>> with patch.dict(foo, {'newkey': 'newvalue'}, clear=True):
...     assert foo == {'newkey': 'newvalue'}
...
>>> assert foo == original

Mock hỗ trợ chế nhạo Python magic methods. Cách dễ nhất để sử dụng các phương thức ma thuật là với lớp MagicMock. Nó cho phép bạn làm những việc như:

>>> mock = MagicMock()
>>> mock.__str__.return_value = 'foobarbaz'
>>> str(mock)
'foobarbaz'
>>> mock.__str__.assert_called_with()

Mock cho phép bạn gán các hàm (hoặc các phiên bản Mock khác) cho các phương thức ma thuật và chúng sẽ được gọi một cách thích hợp. Lớp MagicMock chỉ là một biến thể Mock có tất cả các phương thức ma thuật được tạo sẵn cho bạn (à, tất cả những phương thức hữu ích).

Sau đây là ví dụ về việc sử dụng các phương thức ma thuật với lớp Mock thông thường:

>>> mock = Mock()
>>> mock.__str__ = Mock(return_value='wheeeeee')
>>> str(mock)
'wheeeeee'

Để đảm bảo rằng các đối tượng mô phỏng trong thử nghiệm của bạn có cùng api với các đối tượng mà chúng đang thay thế, bạn có thể sử dụng auto-speccing. Việc tự động xác định có thể được thực hiện thông qua đối số autospec để vá hoặc hàm create_autospec(). Tính năng tự động xác định sẽ tạo các đối tượng mô phỏng có cùng thuộc tính và phương thức với đối tượng mà chúng đang thay thế, đồng thời mọi hàm và phương thức (bao gồm cả hàm tạo) đều có cùng chữ ký cuộc gọi với đối tượng thực.

Điều này đảm bảo rằng các mô hình của bạn sẽ bị lỗi giống như mã sản xuất nếu chúng được sử dụng không đúng cách:

>>> from unittest.mock import create_autospec
>>> def function(a, b, c):
...     pass
...
>>> mock_function = create_autospec(function, return_value='fishy')
>>> mock_function(1, 2, 3)
'fishy'
>>> mock_function.assert_called_once_with(1, 2, 3)
>>> mock_function('wrong arguments')
Traceback (most recent call last):
 ...
TypeError: missing a required argument: 'b'

create_autospec() cũng có thể được sử dụng trên các lớp, trong đó nó sao chép chữ ký của phương thức __init__ và trên các đối tượng có thể gọi được trong đó nó sao chép chữ ký của phương thức __call__.

Lớp giả

Mock là một đối tượng mô phỏng linh hoạt nhằm thay thế việc sử dụng sơ khai và kiểm tra nhân đôi trong toàn bộ mã của bạn. Mô phỏng có thể gọi được và tạo thuộc tính dưới dạng mô phỏng mới khi bạn truy cập chúng [1]. Việc truy cập vào cùng một thuộc tính sẽ luôn trả về cùng một mô hình. Mô hình mô phỏng ghi lại cách bạn sử dụng chúng, cho phép bạn đưa ra xác nhận về những gì mã của bạn đã làm với chúng.

MagicMock là một lớp con của Mock với tất cả các phương thức ma thuật được tạo sẵn và sẵn sàng để sử dụng. Ngoài ra còn có các biến thể không thể gọi được, hữu ích khi bạn mô phỏng các đối tượng không thể gọi được: NonCallableMockNonCallableMagicMock

Trình trang trí patch() giúp dễ dàng thay thế tạm thời các lớp trong một mô-đun cụ thể bằng đối tượng Mock. Theo mặc định, patch() sẽ tạo MagicMock cho bạn. Bạn có thể chỉ định một lớp thay thế của Mock bằng cách sử dụng đối số new_callable cho patch().

class unittest.mock.Mock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)

Tạo một đối tượng Mock mới. Mock nhận một số đối số tùy chọn chỉ định hành vi của đối tượng Mock:

  • spec: Đây có thể là danh sách các chuỗi hoặc một đối tượng hiện có (một lớp hoặc phiên bản) đóng vai trò là đặc tả cho đối tượng mô phỏng. Nếu bạn truyền vào một đối tượng thì một danh sách các chuỗi sẽ được hình thành bằng cách gọi dir trên đối tượng đó (không bao gồm các thuộc tính và phương thức ma thuật không được hỗ trợ). Truy cập bất kỳ thuộc tính nào không có trong danh sách này sẽ tăng AttributeError.

    Nếu spec là một đối tượng (chứ không phải một danh sách các chuỗi) thì __class__ trả về lớp của đối tượng spec. Điều này cho phép các mô phỏng vượt qua các bài kiểm tra isinstance().

  • spec_set: Một biến thể chặt chẽ hơn của spec. Nếu được sử dụng, việc cố gắng set hoặc lấy một thuộc tính trên mô hình không có trên đối tượng được chuyển dưới dạng spec_set sẽ tăng AttributeError.

  • side_effect: Một hàm được gọi bất cứ khi nào Mock được gọi. Xem thuộc tính side_effect. Hữu ích cho việc đưa ra các ngoại lệ hoặc thay đổi giá trị trả về một cách linh hoạt. Hàm được gọi với các đối số giống như hàm mô phỏng và trừ khi nó trả về DEFAULT, giá trị trả về của hàm này sẽ được sử dụng làm giá trị trả về.

    Ngoài ra, side_effect có thể là một lớp hoặc phiên bản ngoại lệ. Trong trường hợp này, ngoại lệ sẽ xuất hiện khi mô hình được gọi.

    Nếu side_effect là một iterable thì mỗi lệnh gọi đến mock sẽ trả về giá trị tiếp theo từ iterable.

    Có thể xóa side_effect bằng cách đặt nó thành None.

  • return_value: Giá trị được trả về khi mô phỏng được gọi. Theo mặc định, đây là Mock mới (được tạo trong lần truy cập đầu tiên). Xem thuộc tính return_value.

  • unsafe: Theo mặc định, việc truy cập bất kỳ thuộc tính nào có tên bắt đầu bằng assert, assret, asert, aseert hoặc assrt sẽ tạo ra AttributeError. Việc vượt qua unsafe=True sẽ cho phép truy cập vào các thuộc tính này.

    Added in version 3.5.

  • wraps: Vật phẩm để bọc đối tượng giả. Nếu wraps không phải là None thì việc gọi Mock sẽ chuyển cuộc gọi đến đối tượng được bao bọc (trả về kết quả thực). Quyền truy cập thuộc tính trên mô hình sẽ trả về một đối tượng Mock bao bọc thuộc tính tương ứng của đối tượng được bao bọc (vì vậy việc cố gắng truy cập một thuộc tính không tồn tại sẽ tạo ra AttributeError).

    Nếu mô hình có tập hợp return_value rõ ràng thì các cuộc gọi sẽ không được chuyển đến đối tượng được bao bọc và thay vào đó, return_value sẽ được trả về.

  • name: Nếu mô hình có tên thì tên đó sẽ được sử dụng trong bản mô phỏng. Điều này có thể hữu ích cho việc gỡ lỗi. Tên được truyền bá cho trẻ em giả.

Mocks cũng có thể được gọi với các đối số từ khóa tùy ý. Chúng sẽ được sử dụng để đặt thuộc tính trên mô hình sau khi nó được tạo. Xem phương pháp configure_mock() để biết chi tiết.

assert_called()

Khẳng định rằng mô hình đã được gọi ít nhất một lần.

>>> mock = Mock()
>>> mock.method()
<Mock name='mock.method()' id='...'>
>>> mock.method.assert_called()

Added in version 3.6.

assert_called_once()

Khẳng định rằng bản mô phỏng đã được gọi đúng một lần.

>>> mock = Mock()
>>> mock.method()
<Mock name='mock.method()' id='...'>
>>> mock.method.assert_called_once()
>>> mock.method()
<Mock name='mock.method()' id='...'>
>>> mock.method.assert_called_once()
Traceback (most recent call last):
...
AssertionError: Expected 'method' to have been called once. Called 2 times.
Calls: [call(), call()].

Added in version 3.6.

assert_called_with(*args, **kwargs)

Phương pháp này là một cách thuận tiện để khẳng định rằng lệnh gọi cuối cùng đã được thực hiện theo một cách cụ thể:

>>> mock = Mock()
>>> mock.method(1, 2, 3, test='wow')
<Mock name='mock.method()' id='...'>
>>> mock.method.assert_called_with(1, 2, 3, test='wow')
assert_called_once_with(*args, **kwargs)

Xác nhận rằng mô hình đã được gọi chính xác một lần và lệnh gọi đó có các đối số đã chỉ định.

>>> mock = Mock(return_value=None)
>>> mock('foo', bar='baz')
>>> mock.assert_called_once_with('foo', bar='baz')
>>> mock('other', bar='values')
>>> mock.assert_called_once_with('other', bar='values')
Traceback (most recent call last):
  ...
AssertionError: Expected 'mock' to be called once. Called 2 times.
Calls: [call('foo', bar='baz'), call('other', bar='values')].
assert_any_call(*args, **kwargs)

khẳng định mô hình đã được gọi với các đối số được chỉ định.

Xác nhận sẽ vượt qua nếu mô hình có ever được gọi, không giống như assert_called_with()assert_called_once_with() chỉ vượt qua nếu cuộc gọi là cuộc gọi gần đây nhất và trong trường hợp assert_called_once_with() thì đó cũng phải là cuộc gọi duy nhất.

>>> mock = Mock(return_value=None)
>>> mock(1, 2, arg='thing')
>>> mock('some', 'thing', 'else')
>>> mock.assert_any_call(1, 2, arg='thing')
assert_has_calls(calls, any_order=False)

khẳng định mô hình đã được gọi với các lệnh gọi được chỉ định. Danh sách mock_calls được kiểm tra các cuộc gọi.

Nếu any_order sai thì các lệnh gọi phải tuần tự. Có thể có thêm cuộc gọi trước hoặc sau cuộc gọi được chỉ định.

Nếu any_order đúng thì các cuộc gọi có thể theo bất kỳ thứ tự nào, nhưng tất cả chúng phải xuất hiện trong mock_calls.

>>> mock = Mock(return_value=None)
>>> mock(1)
>>> mock(2)
>>> mock(3)
>>> mock(4)
>>> calls = [call(2), call(3)]
>>> mock.assert_has_calls(calls)
>>> calls = [call(4), call(2), call(3)]
>>> mock.assert_has_calls(calls, any_order=True)
assert_not_called()

Khẳng định mô hình không bao giờ được gọi.

>>> m = Mock()
>>> m.hello.assert_not_called()
>>> obj = m.hello()
>>> m.hello.assert_not_called()
Traceback (most recent call last):
  ...
AssertionError: Expected 'hello' to not have been called. Called 1 times.
Calls: [call()].

Added in version 3.5.

reset_mock(*, return_value=False, side_effect=False)

Phương thức reset_mock đặt lại tất cả thuộc tính lệnh gọi trên một đối tượng giả:

>>> mock = Mock(return_value=None)
>>> giả ('xin chào')
>>> mock.gọi
đúng
>>> mock.reset_mock()
>>> mock.gọi
sai

Điều này có thể hữu ích khi bạn muốn thực hiện một loạt các xác nhận sử dụng lại cùng một đối tượng.

Tham số return_value khi được đặt thành True sẽ đặt lại return_value:

>>>  phỏng =  phỏng(return_value=5)
>>> giả ('xin chào')
5
>>> mock.reset_mock(return_value=True)
>>> giả ('xin chào')
<Tên giả='mock()' id='...'>

Tham số side_effect khi được đặt thành True sẽ đặt lại side_effect:

>>> mock = Mock(side_effect=ValueError)
>>> giả ('xin chào')
Traceback (cuộc gọi gần đây nhất):
  ...
Giá trịLỗi
>>> mock.reset_mock(side_effect=True)
>>> giả ('xin chào')
<Tên giả='mock()' id='...'>

Lưu ý rằng reset_mock() doesn't xóa các thuộc tính return_value, side_effect hoặc bất kỳ thuộc tính con nào bạn đã đặt bằng cách sử dụng phép gán thông thường theo mặc định.

Các mô hình con cũng được thiết lập lại.

Thay đổi trong phiên bản 3.6: Đã thêm hai đối số chỉ có từ khóa vào hàm reset_mock.

mock_add_spec(spec, spec_set=False)

Thêm thông số kỹ thuật vào mô hình. spec có thể là một đối tượng hoặc một danh sách các chuỗi. Chỉ các thuộc tính trên spec mới có thể được tìm nạp dưới dạng thuộc tính từ mô hình.

Nếu spec_set là true thì chỉ có thể đặt các thuộc tính trên thông số kỹ thuật.

attach_mock(mock, attribute)

Đính kèm một mô hình làm thuộc tính của mô hình này, thay thế tên và cha mẹ của nó. Các cuộc gọi đến mô hình đính kèm sẽ được ghi lại trong thuộc tính method_callsmock_calls của mô hình này.

configure_mock(**kwargs)

Đặt thuộc tính trên mô hình thông qua đối số từ khóa.

Các thuộc tính cộng với giá trị trả về và tác dụng phụ có thể được đặt trên các mô hình con bằng cách sử dụng ký hiệu dấu chấm tiêu chuẩn và giải nén từ điển trong lệnh gọi phương thức:

>>> mock = Mock()
>>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> mock.configure_mock(**attrs)
>>> mock.method()
3
>>> mock.other()
Traceback (most recent call last):
  ...
KeyError

Điều tương tự có thể đạt được trong lệnh gọi hàm tạo tới mock:

>>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> mock = Mock(some_attribute='eggs', **attrs)
>>> mock.some_attribute
'eggs'
>>> mock.method()
3
>>> mock.other()
Traceback (most recent call last):
  ...
KeyError

configure_mock() tồn tại để giúp việc cấu hình dễ dàng hơn sau khi mô hình được tạo.

__dir__()

Đối tượng Mock giới hạn kết quả của dir(some_mock) ở mức hữu ích. Đối với mô hình có spec, điều này bao gồm tất cả các thuộc tính được phép cho mô hình.

Xem FILTER_DIR để biết chức năng của bộ lọc này và cách tắt nó.

_get_child_mock(**kw)

Tạo các mô hình con cho các thuộc tính và giá trị trả về. Theo mặc định, các mô phỏng con sẽ cùng loại với mô hình gốc. Các lớp con của Mock có thể muốn ghi đè điều này để tùy chỉnh cách tạo ra các mô phỏng con.

Đối với các mô hình không thể gọi được, biến thể có thể gọi được sẽ được sử dụng (chứ không phải bất kỳ lớp con tùy chỉnh nào).

called

Một boolean biểu thị liệu đối tượng giả có được gọi hay không:

>>> mock = Mock(return_value=None)
>>> mock.called
False
>>> mock()
>>> mock.called
True
call_count

Một số nguyên cho bạn biết đối tượng giả đã được gọi bao nhiêu lần:

>>> mock = Mock(return_value=None)
>>> mock.call_count
0
>>> mock()
>>> mock()
>>> mock.call_count
2
return_value

Đặt cái này để định cấu hình giá trị được trả về bằng cách gọi mô hình:

>>> mock = Mock()
>>> mock.return_value = 'fish'
>>> mock()
'fish'

Giá trị trả về mặc định là một đối tượng giả và bạn có thể định cấu hình nó theo cách thông thường:

>>> mock = Mock()
>>> mock.return_value.attribute = sentinel.Attribute
>>> mock.return_value()
<Mock name='mock()()' id='...'>
>>> mock.return_value.assert_called_with()

return_value cũng có thể được đặt trong hàm tạo:

>>> mock = Mock(return_value=3)
>>> mock.return_value
3
>>> mock()
3
side_effect

Đây có thể là một hàm được gọi khi mô hình được gọi, một hàm có thể lặp lại hoặc một ngoại lệ (lớp hoặc phiên bản) được đưa ra.

Nếu bạn truyền vào một hàm, nó sẽ được gọi với cùng các đối số như mô hình và trừ khi hàm trả về đơn vị DEFAULT, lệnh gọi đến mô phỏng sẽ trả về bất cứ điều gì hàm trả về. Nếu hàm trả về DEFAULT thì mô hình sẽ trả về giá trị bình thường (từ return_value).

Nếu bạn chuyển vào một iterable, nó sẽ được sử dụng để truy xuất một iterator phải mang lại giá trị cho mỗi lệnh gọi. Giá trị này có thể là một trường hợp ngoại lệ được nâng lên hoặc một giá trị được trả về từ lệnh gọi đến mô hình (việc xử lý DEFAULT giống hệt với trường hợp hàm).

Một ví dụ về mô phỏng đưa ra một ngoại lệ (để kiểm tra việc xử lý ngoại lệ của API):

>>> mock = Mock()
>>> mock.side_effect = Exception('Boom!')
>>> mock()
Traceback (most recent call last):
  ...
Exception: Boom!

Sử dụng side_effect để trả về một chuỗi giá trị:

>>> mock = Mock()
>>> mock.side_effect = [3, 2, 1]
>>> mock(), mock(), mock()
(3, 2, 1)

Sử dụng một cuộc gọi có thể:

>>> mock = Mock(return_value=3)
>>> def side_effect(*args, **kwargs):
...     return DEFAULT
...
>>> mock.side_effect = side_effect
>>> mock()
3

side_effect có thể được đặt trong hàm tạo. Đây là một ví dụ thêm một vào giá trị mà mô hình được gọi và trả về giá trị đó:

>>> side_effect = lambda value: value + 1
>>> mock = Mock(side_effect=side_effect)
>>> mock(3)
4
>>> mock(-8)
-7

Đặt side_effect thành None sẽ xóa nó:

>>> m = Mock(side_effect=KeyError, return_value=3)
>>> m()
Traceback (most recent call last):
 ...
KeyError
>>> m.side_effect = None
>>> m()
3
call_args

Đây là None (nếu mô hình chưa được gọi) hoặc các đối số mà mô hình được gọi lần cuối. Điều này sẽ ở dạng một bộ dữ liệu: thành viên đầu tiên, cũng có thể được truy cập thông qua thuộc tính args, là bất kỳ đối số vị trí nào mà mô hình được gọi bằng (hoặc một bộ dữ liệu trống) và thành viên thứ hai, cũng có thể được truy cập thông qua thuộc tính kwargs, là bất kỳ đối số từ khóa nào (hoặc một từ điển trống).

>>> mock = Mock(return_value=None)
>>> print(mock.call_args)
None
>>> mock()
>>> mock.call_args
call()
>>> mock.call_args == ()
True
>>> mock(3, 4)
>>> mock.call_args
call(3, 4)
>>> mock.call_args == ((3, 4),)
True
>>> mock.call_args.args
(3, 4)
>>> mock.call_args.kwargs
{}
>>> mock(3, 4, 5, key='fish', next='w00t!')
>>> mock.call_args
call(3, 4, 5, key='fish', next='w00t!')
>>> mock.call_args.args
(3, 4, 5)
>>> mock.call_args.kwargs
{'key': 'fish', 'next': 'w00t!'}

call_args, cùng với các thành viên trong danh sách call_args_list, method_callsmock_calls là các đối tượng call. Đây là các bộ dữ liệu, vì vậy chúng có thể được giải nén để lấy các đối số riêng lẻ và đưa ra các xác nhận phức tạp hơn. Xem calls as tuples.

Thay đổi trong phiên bản 3.8: Đã thêm thuộc tính argskwargs.

call_args_list

Đây là danh sách tất cả các lệnh gọi được thực hiện đến đối tượng giả theo trình tự (vì vậy độ dài của danh sách là số lần nó được gọi). Trước khi thực hiện bất kỳ cuộc gọi nào, đó là một danh sách trống. Đối tượng call có thể được sử dụng để xây dựng danh sách các lệnh gọi một cách thuận tiện để so sánh với call_args_list.

>>> mock = Mock(return_value=None)
>>> mock()
>>> mock(3, 4)
>>> mock(key='fish', next='w00t!')
>>> mock.call_args_list
[call(), call(3, 4), call(key='fish', next='w00t!')]
>>> expected = [(), ((3, 4),), ({'key': 'fish', 'next': 'w00t!'},)]
>>> mock.call_args_list == expected
True

Các thành viên của call_args_list là các đối tượng call. Chúng có thể được giải nén dưới dạng bộ dữ liệu để lấy các đối số riêng lẻ. Xem calls as tuples.

method_calls

Ngoài việc theo dõi các cuộc gọi đến chính chúng, mô phỏng cũng theo dõi các cuộc gọi đến các phương thức và thuộc tính cũng như các phương thức và thuộc tính their:

>>> mock = Mock()
>>> mock.method()
<Mock name='mock.method()' id='...'>
>>> mock.property.method.attribute()
<Mock name='mock.property.method.attribute()' id='...'>
>>> mock.method_calls
[call.method(), call.property.method.attribute()]

Các thành viên của method_calls là các đối tượng call. Chúng có thể được giải nén dưới dạng bộ dữ liệu để lấy các đối số riêng lẻ. Xem calls as tuples.

mock_calls

mock_calls ghi lại các lệnh gọi all đến đối tượng giả, các phương thức của nó, các phương thức ma thuật and trả về giá trị giả.

>>> mock = MagicMock()
>>> result = mock(1, 2, 3)
>>> mock.first(a=3)
<MagicMock name='mock.first()' id='...'>
>>> mock.second()
<MagicMock name='mock.second()' id='...'>
>>> int(mock)
1
>>> result(1)
<MagicMock name='mock()()' id='...'>
>>> expected = [call(1, 2, 3), call.first(a=3), call.second(),
... call.__int__(), call()(1)]
>>> mock.mock_calls == expected
True

Các thành viên của mock_calls là các đối tượng call. Chúng có thể được giải nén dưới dạng bộ dữ liệu để lấy các đối số riêng lẻ. Xem calls as tuples.

Ghi chú

Cách mock_calls được ghi lại có nghĩa là khi thực hiện các cuộc gọi lồng nhau, các tham số của cuộc gọi tổ tiên sẽ không được ghi lại và do đó sẽ luôn so sánh bằng nhau:

>>> mock = MagicMock()
>>> mock.top(a=3).bottom()
<MagicMock name='mock.top().bottom()' id='...'>
>>> mock.mock_calls
[call.top(a=3), call.top().bottom()]
>>> mock.mock_calls[-1] == call.top(a=-1).bottom()
True
__class__

Thông thường thuộc tính __class__ của một đối tượng sẽ trả về kiểu của nó. Đối với một đối tượng mô phỏng có spec, __class__ sẽ trả về lớp spec. Điều này cho phép các đối tượng giả vượt qua các bài kiểm tra isinstance() cho đối tượng mà chúng đang thay thế/giả dạng:

>>> mock = Mock(spec=3)
>>> isinstance(mock, int)
True

__class__ có thể được gán cho, điều này cho phép mô phỏng vượt qua kiểm tra isinstance() mà không buộc bạn phải sử dụng thông số kỹ thuật:

>>> mock = Mock()
>>> mock.__class__ = dict
>>> isinstance(mock, dict)
True
class unittest.mock.NonCallableMock(spec=None, wraps=None, name=None, spec_set=None, **kwargs)

Một phiên bản không thể gọi được của Mock. Các tham số của hàm tạo có cùng ý nghĩa với Mock, ngoại trừ return_valueside_effect không có ý nghĩa gì trên một mô hình không thể gọi được.

Các đối tượng mô phỏng sử dụng một lớp hoặc một thể hiện làm spec hoặc spec_set có thể vượt qua các bài kiểm tra isinstance():

>>> mock = Mock(spec=SomeClass)
>>> isinstance(mock, SomeClass)
True
>>> mock = Mock(spec_set=SomeClass())
>>> isinstance(mock, SomeClass)
True

Các lớp Mock có hỗ trợ các phương pháp ma thuật chế nhạo. Xem magic methods để biết chi tiết đầy đủ.

Các lớp mô phỏng và trình trang trí patch() đều lấy các đối số từ khóa tùy ý để cấu hình. Đối với trình trang trí patch(), các từ khóa được chuyển đến hàm tạo của mô hình đang được tạo. Các đối số từ khóa dùng để định cấu hình các thuộc tính của mô hình:

>>> m = MagicMock(attribute=3, other='fish')
>>> m.attribute
3
>>> m.other
'fish'

Giá trị trả về và tác dụng phụ của các mô phỏng con có thể được đặt theo cách tương tự, sử dụng ký hiệu chấm. Vì bạn không thể sử dụng tên có dấu chấm trực tiếp trong cuộc gọi nên bạn phải tạo một từ điển và giải nén nó bằng **:

>>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> mock = Mock(some_attribute='eggs', **attrs)
>>> mock.some_attribute
'eggs'
>>> mock.method()
3
>>> mock.other()
Traceback (most recent call last):
  ...
KeyError

Một mô hình có thể gọi được được tạo bằng spec (hoặc spec_set) sẽ xem xét chữ ký của đối tượng đặc tả khi khớp các lệnh gọi với mô hình. Do đó, nó có thể khớp với các đối số của lệnh gọi thực tế bất kể chúng được truyền theo vị trí hay theo tên:

>>> def f(a, b, c): đạt
...
>>>  phỏng =  phỏng(spec=f)
>>> giả (1, 2, c=3)
<Tên giả='mock()' id='140161580456576'>
>>> mock.assert_gọi_with(1, 2, 3)
>>> mock.assert_gọi_with(a=1, b=2, c=3)

Điều này áp dụng cho assert_called_with(), assert_called_once_with(), assert_has_calls()assert_any_call(). Khi Tự động xác định, nó cũng sẽ áp dụng cho các lệnh gọi phương thức trên đối tượng giả.

Thay đổi trong phiên bản 3.4: Đã thêm tính năng xem xét nội tâm chữ ký trên các đối tượng mô phỏng được xác định và xác định tự động.

class unittest.mock.PropertyMock(*args, **kwargs)

Một mô hình nhằm mục đích sử dụng làm property hoặc descriptor khác trên một lớp. PropertyMock cung cấp các phương thức __get__()__set__() để bạn có thể chỉ định giá trị trả về khi nó được tìm nạp.

Việc tìm nạp một phiên bản PropertyMock từ một đối tượng sẽ gọi mô hình mà không có đối số. Việc đặt nó sẽ gọi mô hình với giá trị được đặt.

>>> lớp Foo:
... @property
... def foo(tự):
... trả lại 'thứ gì đó'
... @foo.setter
... def foo(tự, giá trị):
... vượt qua
...
>>> với patch('__main__.Foo.foo', new_callable=PropertyMock)  mock_foo:
... mock_foo.return_value = 'mockity-mock'
... this_foo = Foo()
... in(this_foo.foo)
... this_foo.foo = 6
...
nhạo báng-chế giễu
>>> mock_foo.mock_calls
[gọi(), gọi(6)]

Do cách lưu trữ các thuộc tính mô phỏng nên bạn không thể đính kèm trực tiếp PropertyMock vào một đối tượng mô phỏng. Thay vào đó, bạn có thể đính kèm nó vào đối tượng kiểu giả:

>>> m = MagicMock()
>>> p = PropertyMock(return_value=3)
>>> (m).foo = p
>>> m.foo
3
>>> p.assert_called_once_with()

Cảnh báo

Nếu một AttributeError được PropertyMock nâng lên, nó sẽ được hiểu là một bộ mô tả bị thiếu và __getattr__() sẽ được gọi trên mô hình gốc:

>>> m = MagicMock()
>>> no_attribute = PropertyMock(side_effect=AttributionError)
>>> type(m).my_property = no_attribute
>>> m.my_property
<MagicMock name='mock.my_property' id='140165240345424'>

Xem __getattr__() để biết chi tiết.

class unittest.mock.AsyncMock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)

Phiên bản không đồng bộ của MagicMock. Đối tượng AsyncMock sẽ hoạt động để đối tượng được nhận dạng là hàm không đồng bộ và kết quả của cuộc gọi là có thể chờ đợi.

>>> mock = AsyncMock()
>>> inspect.iscoroutinefunction(mock)
True
>>> inspect.isawaitable(mock())
True

Kết quả của mock() là một hàm không đồng bộ sẽ có kết quả là side_effect hoặc return_value sau khi được chờ đợi:

  • nếu side_effect là một hàm thì hàm async sẽ trả về kết quả của hàm đó,

  • nếu side_effect là một ngoại lệ, hàm async sẽ đưa ra ngoại lệ đó,

  • nếu side_effect là một iterable, hàm async sẽ trả về giá trị tiếp theo của iterable, tuy nhiên, nếu hết chuỗi kết quả, StopAsyncIteration sẽ được nâng lên ngay lập tức,

  • nếu side_effect không được xác định, hàm async sẽ trả về giá trị được xác định bởi return_value, do đó, theo mặc định, hàm async trả về một đối tượng AsyncMock mới.

Việc đặt spec của Mock hoặc MagicMock thành hàm không đồng bộ sẽ dẫn đến việc trả về một đối tượng coroutine sau khi gọi.

>>> async def async_func(): pass
...
>>> mock = MagicMock(async_func)
>>> mock
<MagicMock spec='function' id='...'>
>>> mock()
<coroutine object AsyncMockMixin._mock_call at ...>

Việc đặt spec của Mock, MagicMock hoặc AsyncMock thành một lớp có các hàm không đồng bộ và đồng bộ sẽ tự động phát hiện các hàm đồng bộ và đặt chúng là MagicMock (nếu mô hình gốc là AsyncMock hoặc MagicMock) hoặc Mock (nếu mô hình gốc là Mock). Tất cả các hàm không đồng bộ sẽ là AsyncMock.

>>> class ExampleClass:
...     def sync_foo():
...         pass
...     async def async_foo():
...         pass
...
>>> a_mock = AsyncMock(ExampleClass)
>>> a_mock.sync_foo
<MagicMock name='mock.sync_foo' id='...'>
>>> a_mock.async_foo
<AsyncMock name='mock.async_foo' id='...'>
>>> mock = Mock(ExampleClass)
>>> mock.sync_foo
<Mock name='mock.sync_foo' id='...'>
>>> mock.async_foo
<AsyncMock name='mock.async_foo' id='...'>

Added in version 3.8.

assert_awaited()

Khẳng định rằng bản mô phỏng đã được chờ đợi ít nhất một lần. Lưu ý rằng điều này tách biệt với đối tượng được gọi, phải sử dụng từ khóa await:

>>> mock = AsyncMock()
>>> async def main(coroutine_mock):
...     await coroutine_mock
...
>>> coroutine_mock = mock()
>>> mock.called
True
>>> mock.assert_awaited()
Traceback (most recent call last):
...
AssertionError: Expected mock to have been awaited.
>>> asyncio.run(main(coroutine_mock))
>>> mock.assert_awaited()
assert_awaited_once()

Khẳng định rằng bản mô phỏng đã được chờ đúng một lần.

>>> mock = AsyncMock()
>>> async def main():
...     await mock()
...
>>> asyncio.run(main())
>>> mock.assert_awaited_once()
>>> asyncio.run(main())
>>> mock.assert_awaited_once()
Traceback (most recent call last):
...
AssertionError: Expected mock to have been awaited once. Awaited 2 times.
assert_awaited_with(*args, **kwargs)

Khẳng định rằng lần chờ đợi cuối cùng là với các đối số đã chỉ định.

>>> mock = AsyncMock()
>>> async def main(*args, **kwargs):
...     await mock(*args, **kwargs)
...
>>> asyncio.run(main('foo', bar='bar'))
>>> mock.assert_awaited_with('foo', bar='bar')
>>> mock.assert_awaited_with('other')
Traceback (most recent call last):
...
AssertionError: expected await not found.
Expected: mock('other')
Actual: mock('foo', bar='bar')
assert_awaited_once_with(*args, **kwargs)

Xác nhận rằng bản mô phỏng đã được chờ chính xác một lần và với các đối số được chỉ định.

>>> mock = AsyncMock()
>>> async def main(*args, **kwargs):
...     await mock(*args, **kwargs)
...
>>> asyncio.run(main('foo', bar='bar'))
>>> mock.assert_awaited_once_with('foo', bar='bar')
>>> asyncio.run(main('foo', bar='bar'))
>>> mock.assert_awaited_once_with('foo', bar='bar')
Traceback (most recent call last):
...
AssertionError: Expected mock to have been awaited once. Awaited 2 times.
assert_any_await(*args, **kwargs)

Khẳng định mô hình đã từng được chờ đợi với các đối số được chỉ định.

>>> mock = AsyncMock()
>>> async def main(*args, **kwargs):
...     await mock(*args, **kwargs)
...
>>> asyncio.run(main('foo', bar='bar'))
>>> asyncio.run(main('hello'))
>>> mock.assert_any_await('foo', bar='bar')
>>> mock.assert_any_await('other')
Traceback (most recent call last):
...
AssertionError: mock('other') await not found
assert_has_awaits(calls, any_order=False)

Xác nhận bản mô phỏng đã được chờ đợi với các lệnh gọi được chỉ định. Danh sách await_args_list được kiểm tra để chờ.

Nếu any_order sai thì việc chờ đợi phải tuần tự. Có thể có thêm cuộc gọi trước hoặc sau thời gian chờ được chỉ định.

Nếu any_order là đúng thì sự chờ đợi có thể theo bất kỳ thứ tự nào, nhưng tất cả chúng phải xuất hiện trong await_args_list.

>>> mock = AsyncMock()
>>> async def main(*args, **kwargs):
...     await mock(*args, **kwargs)
...
>>> calls = [call("foo"), call("bar")]
>>> mock.assert_has_awaits(calls)
Traceback (most recent call last):
...
AssertionError: Awaits not found.
Expected: [call('foo'), call('bar')]
Actual: []
>>> asyncio.run(main('foo'))
>>> asyncio.run(main('bar'))
>>> mock.assert_has_awaits(calls)
assert_not_awaited()

Khẳng định rằng mô hình không bao giờ được chờ đợi.

>>> mock = AsyncMock()
>>> mock.assert_not_awaited()
reset_mock(*args, **kwargs)

Xem Mock.reset_mock(). Đồng thời đặt await_count thành 0, await_args thành None và xóa await_args_list.

await_count

Một số nguyên theo dõi số lần đối tượng giả đã được chờ đợi.

>>> mock = AsyncMock()
>>> async def main():
...     await mock()
...
>>> asyncio.run(main())
>>> mock.await_count
1
>>> asyncio.run(main())
>>> mock.await_count
2
await_args

Đây là None (nếu bản mô phỏng chưa được chờ đợi) hoặc các đối số mà bản mô phỏng được chờ đợi lần cuối. Chức năng tương tự như Mock.call_args.

>>> mock = AsyncMock()
>>> async def main(*args):
...     await mock(*args)
...
>>> mock.await_args
>>> asyncio.run(main('foo'))
>>> mock.await_args
call('foo')
>>> asyncio.run(main('bar'))
>>> mock.await_args
call('bar')
await_args_list

Đây là danh sách tất cả các lần chờ được thực hiện cho đối tượng mô phỏng theo trình tự (vì vậy độ dài của danh sách là số lần nó được chờ đợi). Trước khi thực hiện bất kỳ sự chờ đợi nào, đó là một danh sách trống.

>>> mock = AsyncMock()
>>> async def main(*args):
...     await mock(*args)
...
>>> mock.await_args_list
[]
>>> asyncio.run(main('foo'))
>>> mock.await_args_list
[call('foo')]
>>> asyncio.run(main('bar'))
>>> mock.await_args_list
[call('foo'), call('bar')]
class unittest.mock.ThreadingMock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, *, timeout=UNSET, **kwargs)

Một phiên bản MagicMock để kiểm tra đa luồng. Đối tượng ThreadingMock cung cấp các phương thức bổ sung để chờ một cuộc gọi được gọi, thay vì xác nhận nó ngay lập tức.

Thời gian chờ mặc định được chỉ định bởi đối số timeout hoặc nếu không được đặt bởi thuộc tính ThreadingMock.DEFAULT_TIMEOUT thì mặc định là chặn (None).

Bạn có thể định cấu hình thời gian chờ mặc định chung bằng cách đặt ThreadingMock.DEFAULT_TIMEOUT.

wait_until_called(*, timeout=UNSET)

Chờ cho đến khi mô phỏng được gọi.

Nếu đã hết thời gian chờ khi tạo mô hình hoặc nếu đối số hết thời gian chờ được chuyển cho hàm này, thì hàm sẽ tăng AssertionError nếu lệnh gọi không được thực hiện kịp thời.

>>> mock = ThreadingMock()
>>> thread = threading.Thread(target=mock)
>>> thread.start()
>>> mock.wait_until_called(timeout=1)
>>> thread.join()
wait_until_any_call_with(*args, **kwargs)

Đợi cho đến khi mô hình được gọi với các đối số đã chỉ định.

Nếu đã hết thời gian chờ khi tạo mô hình, hàm sẽ tăng AssertionError nếu cuộc gọi không được thực hiện kịp thời.

>>> mock = ThreadingMock()
>>> thread = threading.Thread(target=mock, args=("arg1", "arg2",), kwargs={"arg": "thing"})
>>> thread.start()
>>> mock.wait_until_any_call_with("arg1", "arg2", arg="thing")
>>> thread.join()
DEFAULT_TIMEOUT

Thời gian chờ mặc định chung tính bằng giây để tạo phiên bản ThreadingMock.

Added in version 3.13.

Đang gọi

Các đối tượng giả có thể gọi được. Cuộc gọi sẽ trả về giá trị được đặt làm thuộc tính return_value. Giá trị trả về mặc định là một đối tượng Mock mới; nó được tạo vào lần đầu tiên giá trị trả về được truy cập (rõ ràng hoặc bằng cách gọi Mock) - nhưng nó được lưu trữ và cùng một giá trị được trả về mỗi lần.

Các cuộc gọi đến đối tượng sẽ được ghi lại trong các thuộc tính như call_argscall_args_list.

Nếu side_effect được đặt thì nó sẽ được gọi sau khi cuộc gọi được ghi âm, vì vậy nếu side_effect đưa ra một ngoại lệ thì cuộc gọi vẫn được ghi âm.

Cách đơn giản nhất để tạo một mô hình đưa ra một ngoại lệ khi được gọi là tạo side_effect một lớp hoặc phiên bản ngoại lệ:

>>> m = MagicMock(side_effect=IndexError)
>>> m(1, 2, 3)
Traceback (most recent call last):
  ...
IndexError
>>> m.mock_calls
[call(1, 2, 3)]
>>> m.side_effect = KeyError('Bang!')
>>> m('two', 'three', 'four')
Traceback (most recent call last):
  ...
KeyError: 'Bang!'
>>> m.mock_calls
[call(1, 2, 3), call('two', 'three', 'four')]

Nếu side_effect là một hàm thì bất cứ thứ gì hàm đó trả về đều là những gì gọi đến hàm trả về giả. Hàm side_effect được gọi với các đối số giống như hàm mô phỏng. Điều này cho phép bạn thay đổi giá trị trả về của cuộc gọi một cách linh hoạt, dựa trên đầu vào:

>>> def side_effect(value):
...     return value + 1
...
>>> m = MagicMock(side_effect=side_effect)
>>> m(1)
2
>>> m(2)
3
>>> m.mock_calls
[call(1), call(2)]

Nếu bạn muốn mô hình vẫn trả về giá trị trả về mặc định (mô hình mới) hoặc bất kỳ giá trị trả về nào đã đặt thì có hai cách để thực hiện việc này. Hoặc trả về return_value từ bên trong side_effect hoặc trả về DEFAULT:

>>> m = MagicMock()
>>> def side_effect(*args, **kwargs):
...     return m.return_value
...
>>> m.side_effect = side_effect
>>> m.return_value = 3
>>> m()
3
>>> def side_effect(*args, **kwargs):
...     return DEFAULT
...
>>> m.side_effect = side_effect
>>> m()
3

Để xóa side_effect và quay lại hoạt động mặc định, hãy đặt side_effect thành None:

>>> m = MagicMock(return_value=6)
>>> def side_effect(*args, **kwargs):
...     return 3
...
>>> m.side_effect = side_effect
>>> m()
3
>>> m.side_effect = None
>>> m()
6

Zz000zz cũng có thể là bất kỳ đối tượng có thể lặp lại nào. Các lệnh gọi lặp lại tới mô hình sẽ trả về các giá trị từ iterable (cho đến khi iterable hết và StopIteration được nâng lên):

>>> m = MagicMock(side_effect=[1, 2, 3])
>>> m()
1
>>> m()
2
>>> m()
3
>>> m()
Traceback (most recent call last):
  ...
StopIteration

Nếu bất kỳ thành viên nào của iterable là ngoại lệ, chúng sẽ được nâng lên thay vì được trả về

>>> iterable = (33, ValueError, 66)
>>> m = MagicMock(side_effect=iterable)
>>> m()
33
>>> m()
Traceback (cuộc gọi gần đây nhất):
 ...
Giá trịLỗi
>>> m()
66

Xóa thuộc tính

Các đối tượng giả tạo các thuộc tính theo yêu cầu. Điều này cho phép họ giả vờ là đối tượng thuộc bất kỳ loại nào.

Bạn có thể muốn một đối tượng mô phỏng trả về False cho lệnh gọi hasattr() hoặc tăng AttributeError khi một thuộc tính được tìm nạp. Bạn có thể thực hiện việc này bằng cách cung cấp một đối tượng dưới dạng spec cho mô hình nhưng điều đó không phải lúc nào cũng thuận tiện.

Bạn "chặn" thuộc tính bằng cách xóa chúng. Sau khi xóa, việc truy cập vào một thuộc tính sẽ tăng AttributeError.

>>> mock = MagicMock()
>>> hasattr(mock, 'm')
True
>>> del mock.m
>>> hasattr(mock, 'm')
False
>>> del mock.f
>>> mock.f
Traceback (most recent call last):
    ...
AttributeError: f

Tên giả và thuộc tính tên

Vì "name" là một đối số cho hàm tạo Mock, nên nếu bạn muốn đối tượng giả của mình có thuộc tính "name", bạn không thể chuyển nó vào lúc tạo. Có hai lựa chọn thay thế. Một lựa chọn là sử dụng configure_mock():

>>> giả = MagicMock()
>>> mock.configure_mock(name='my_name')
>>> mock.name
'tên_tôi'

Một tùy chọn đơn giản hơn là chỉ cần đặt thuộc tính "name" sau khi tạo mô hình

>>> giả = MagicMock()
>>> mock.name = "foo"

Đính kèm Mocks làm thuộc tính

Khi bạn đính kèm một mô hình làm thuộc tính của một mô hình khác (hoặc dưới dạng giá trị trả về), nó sẽ trở thành "con" của mô hình đó. Các cuộc gọi đến con được ghi lại trong thuộc tính method_callsmock_calls của cha mẹ. Điều này rất hữu ích để định cấu hình các mô phỏng con và sau đó gắn chúng vào cha mẹ hoặc để gắn các mô hình vào cha mẹ ghi lại tất cả các cuộc gọi đến con cái và cho phép bạn đưa ra các xác nhận về thứ tự các cuộc gọi giữa các mô hình:

>>> parent = MagicMock()
>>> child1 = MagicMock(return_value=None)
>>> child2 = MagicMock(return_value=None)
>>> parent.child1 = child1
>>> parent.child2 = child2
>>> child1(1)
>>> child2(2)
>>> parent.mock_calls
[call.child1(1), call.child2(2)]

Ngoại lệ cho trường hợp này là nếu mô hình có tên. Điều này cho phép bạn ngăn chặn việc "nuôi dạy con cái" nếu vì lý do nào đó mà bạn không muốn điều đó xảy ra.

>>> mock = MagicMock()
>>> not_a_child = MagicMock(name='not-a-child')
>>> mock.attribute = not_a_child
>>> mock.attribute()
<MagicMock name='not-a-child()' id='...'>
>>> mock.mock_calls
[]

Các mô hình do patch() tạo cho bạn sẽ được đặt tên tự động. Để đính kèm các mô hình có tên vào cha mẹ, bạn sử dụng phương thức attach_mock()

>>> điều1 = đối tượng()
>>> điều2 = đối tượng()
>>> cha mẹ = MagicMock()
>>> với patch('__main__.thing1', return_value=None)  child1:
... với patch('__main__.thing2', return_value=None)  child2:
... parent.attach_mock(child1, 'child1')
... parent.attach_mock(child2, 'child2')
... con1('một')
... child2('hai')
...
>>> parent.mock_calls
[call.child1('một'), call.child2('hai')]

Những người vá lỗi

Công cụ trang trí bản vá chỉ được sử dụng để vá các đối tượng trong phạm vi chức năng mà chúng trang trí. Họ tự động xử lý việc hủy vá lỗi cho bạn, ngay cả khi có trường hợp ngoại lệ xảy ra. Tất cả các hàm này cũng có thể được sử dụng trong các câu lệnh hoặc như các hàm trang trí lớp.

Ghi chú

Điều quan trọng là thực hiện vá lỗi trong không gian tên phù hợp. Xem phần where to patch.

unittest.mock.patch(target, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)

patch() hoạt động như một trình trang trí hàm, trình trang trí lớp hoặc trình quản lý bối cảnh. Bên trong phần thân của hàm hoặc câu lệnh, target được vá bằng đối tượng new. Khi câu lệnh function/with thoát, bản vá sẽ được hoàn tác.

Nếu new bị bỏ qua thì mục tiêu sẽ được thay thế bằng AsyncMock nếu đối tượng được vá là một hàm không đồng bộ hoặc MagicMock nếu không. Nếu patch() được sử dụng làm trang trí và new bị bỏ qua, thì mô hình đã tạo sẽ được chuyển vào dưới dạng đối số bổ sung cho hàm được trang trí. Nếu patch() được sử dụng làm trình quản lý bối cảnh thì mô hình đã tạo sẽ được trình quản lý bối cảnh trả về.

target phải là một chuỗi có dạng 'package.module.ClassName'. target được nhập và đối tượng được chỉ định được thay thế bằng đối tượng new, vì vậy target phải có thể nhập được từ môi trường mà bạn đang gọi patch(). Mục tiêu được nhập khi chức năng trang trí được thực thi, không phải lúc trang trí.

Các đối số từ khóa specspec_set được chuyển tới MagicMock nếu bản vá đang tạo một đối số cho bạn.

Ngoài ra, bạn có thể chuyển spec=True hoặc spec_set=True, điều này khiến bản vá được chuyển vào đối tượng bị mô phỏng là đối tượng spec/spec_set.

new_callable cho phép bạn chỉ định một lớp khác hoặc đối tượng có thể gọi được, sẽ được gọi để tạo đối tượng new. Theo mặc định, AsyncMock được sử dụng cho các chức năng không đồng bộ và MagicMock cho các chức năng còn lại.

Một dạng spec mạnh mẽ hơn là autospec. Nếu bạn đặt autospec=True thì mô hình sẽ được tạo với thông số kỹ thuật từ đối tượng được thay thế. All attributes of the mock will also have the spec of the corresponding attribute of the object being replaced. Các phương thức và hàm bị mô phỏng sẽ được kiểm tra các đối số của chúng và sẽ đưa ra TypeError nếu chúng được gọi bằng chữ ký sai. Đối với các mô hình thay thế một lớp, giá trị trả về của chúng ("thể hiện") sẽ có cùng thông số kỹ thuật với lớp đó. Xem chức năng create_autospec()Tự động xác định.

Thay vì autospec=True, bạn có thể chuyển autospec=some_object để sử dụng một đối tượng tùy ý làm thông số kỹ thuật thay vì đối tượng được thay thế.

Theo mặc định, patch() sẽ không thể thay thế các thuộc tính không tồn tại. Nếu bạn chuyển vào create=True và thuộc tính không tồn tại, bản vá sẽ tạo thuộc tính cho bạn khi hàm đã vá được gọi và xóa lại sau khi hàm đã vá đã thoát. Điều này hữu ích cho việc viết bài kiểm tra đối với các thuộc tính mà mã sản xuất của bạn tạo ra khi chạy. Nó được tắt theo mặc định vì nó có thể nguy hiểm. Khi nó được bật, bạn có thể viết các bài kiểm tra vượt qua đối với các API không thực sự tồn tại!

Ghi chú

Thay đổi trong phiên bản 3.5: Nếu bạn đang vá các phần dựng sẵn trong một mô-đun thì bạn không cần chuyển create=True, nó sẽ được thêm theo mặc định.

Bản vá có thể được sử dụng như một công cụ trang trí lớp TestCase. Nó hoạt động bằng cách trang trí từng phương pháp kiểm tra trong lớp. Điều này làm giảm mã soạn sẵn khi các phương pháp thử nghiệm của bạn chia sẻ một bộ bản vá chung. patch() tìm các bài kiểm tra bằng cách tìm kiếm các tên phương thức bắt đầu bằng patch.TEST_PREFIX. Theo mặc định, đây là 'test', phù hợp với cách unittest tìm các bài kiểm tra. Bạn có thể chỉ định tiền tố thay thế bằng cách đặt patch.TEST_PREFIX.

Bản vá có thể được sử dụng làm trình quản lý bối cảnh, với câu lệnh with. Ở đây, bản vá áp dụng cho khối thụt lề sau câu lệnh with. Nếu bạn sử dụng "as" thì đối tượng được vá sẽ bị ràng buộc với tên sau "as"; rất hữu ích nếu patch() đang tạo một đối tượng giả cho bạn.

patch() lấy đối số từ khóa tùy ý. Chúng sẽ được chuyển tới AsyncMock nếu đối tượng được vá không đồng bộ, tới MagicMock nếu không hoặc tới new_callable nếu được chỉ định.

patch.dict(...), patch.multiple(...)patch.object(...) có sẵn cho các trường hợp sử dụng khác.

patch() làm công cụ trang trí hàm, tạo mô hình cho bạn và chuyển nó vào hàm được trang trí:

>>> @patch('__main__.SomeClass')
... hàm def(normal_argument, mock_class):
... print(mock_class  SomeClass)
...
>>> chức năng(Không )
đúng

Việc vá một lớp sẽ thay thế lớp đó bằng MagicMock instance. Nếu lớp được khởi tạo trong mã đang được kiểm tra thì đó sẽ là return_value của mô hình sẽ được sử dụng.

Nếu lớp được khởi tạo nhiều lần, bạn có thể sử dụng side_effect để trả về một bản mô phỏng mới mỗi lần. Ngoài ra, bạn có thể đặt return_value thành bất cứ thứ gì bạn muốn.

Để định cấu hình giá trị trả về trên các phương thức của instances trên lớp được vá, bạn phải thực hiện việc này trên return_value. Ví dụ:

>>> lớp Lớp:
... phương thức def (tự):
... vượt qua
...
>>> với patch('__main__.Class')  MockClass:
... instance = MockClass.return_value
... instance.method.return_value = 'foo'
... khẳng định Class()  phiên bản
... khẳng định Class().method() == 'foo'
...

Nếu bạn sử dụng spec hoặc spec_setpatch() đang thay thế class thì giá trị trả về của mô hình đã tạo sẽ có cùng thông số kỹ thuật.

>>> Bản gốc = Lớp
>>> patcher = patch('__main__.Class', spec=True)
>>> MockClass = patcher.start()
>>>  dụ = MockClass()
>>> khẳng định isinstance(instance, Original)
>>> patcher.stop()

Đối số new_callable rất hữu ích khi bạn muốn sử dụng một lớp thay thế cho MagicMock mặc định cho mô hình đã tạo. Ví dụ: nếu bạn muốn sử dụng NonCallableMock

>>> điều = đối tượng()
>>> với patch('__main__.thing', new_callable=NonCallableMock)  mock_thing:
... khẳng định điều đó  mock_thing
... điều()
...
Traceback (cuộc gọi gần đây nhất):
  ...
TypeError: Không thể gọi được đối tượng 'NonCallableMock'

Một trường hợp sử dụng khác có thể là thay thế một đối tượng bằng một phiên bản io.StringIO

>>> từ io nhập StringIO
>>> def foo():
... print('Cái gì đó')
...
>>> @patch('sys.stdout', new_callable=StringIO)
... kiểm tra chắc chắn (mock_stdout):
... foo()
... khẳng định mock_stdout.getvalue() == 'Cái gì đó\n'
...
>>> kiểm tra()

Khi patch() tạo mô hình cho bạn, thông thường điều đầu tiên bạn cần làm là định cấu hình mô hình. Một số cấu hình đó có thể được thực hiện trong lệnh gọi bản vá. Bất kỳ từ khóa tùy ý nào bạn chuyển vào cuộc gọi sẽ được sử dụng để đặt thuộc tính trên mô hình đã tạo

>>> patcher = patch('__main__.thing', first='one', two='two')
>>> mock_thing = patcher.start()
>>> mock_thing.first
'một'
>>> mock_thing.second
'hai'

Cũng như các thuộc tính trên các thuộc tính mô phỏng đã tạo, như return_valueside_effect, của các mô hình con cũng có thể được định cấu hình. Chúng không hợp lệ về mặt cú pháp để chuyển trực tiếp dưới dạng đối số từ khóa, nhưng một từ điển có các khóa này vẫn có thể được mở rộng thành lệnh gọi patch() bằng cách sử dụng **:

>>> config = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> patcher = patch('__main__.thing', **config)
>>> mock_thing = patcher.start()
>>> mock_thing.method()
3
>>> mock_thing.other()
Traceback (cuộc gọi gần đây nhất):
  ...
Lỗi phím

Theo mặc định, việc cố gắng vá một hàm trong mô-đun (hoặc một phương thức hoặc thuộc tính trong một lớp) không tồn tại sẽ không thành công với AttributeError:

>>> @patch('sys.non_being_attribute', 42)
... kiểm tra chắc chắn():
... khẳng định sys.non_being_attribute == 42
...
>>> kiểm tra()
Traceback (cuộc gọi gần đây nhất):
  ...
AttributionError: <module 'sys' (tích hợp sẵn)> không có thuộc tính 'non_being_attribute'

nhưng việc thêm create=True vào lệnh gọi tới patch() sẽ làm cho ví dụ trước hoạt động như mong đợi:

>>> @patch('sys.non_being_attribute', 42, create=True)
... kiểm tra chắc chắn (mock_stdout):
... khẳng định sys.non_being_attribute == 42
...
>>> kiểm tra()

Thay đổi trong phiên bản 3.8: patch() hiện trả về AsyncMock nếu mục tiêu là hàm không đồng bộ.

patch.object

patch.object(target, attribute, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)

vá thành viên được đặt tên (attribute) trên một đối tượng (target) bằng một đối tượng giả.

patch.object() có thể được sử dụng làm công cụ trang trí, trang trí lớp hoặc trình quản lý bối cảnh. Các đối số new, spec, create, spec_set, autospecnew_callable có cùng ý nghĩa như đối với patch(). Giống như patch(), patch.object() lấy các đối số từ khóa tùy ý để định cấu hình đối tượng giả mà nó tạo.

Khi được sử dụng làm công cụ trang trí lớp, patch.object() vinh danh patch.TEST_PREFIX vì đã chọn phương pháp nào để bọc.

Bạn có thể gọi patch.object() bằng ba đối số hoặc hai đối số. Dạng ba đối số lấy đối tượng cần vá, tên thuộc tính và đối tượng để thay thế thuộc tính đó.

Khi gọi bằng hai dạng đối số, bạn bỏ qua đối tượng thay thế và một mô hình được tạo cho bạn và chuyển vào dưới dạng đối số bổ sung cho hàm được trang trí:

>>> @patch.object(SomeClass, 'class_method')
... def test(mock_method):
...     SomeClass.class_method(3)
...     mock_method.assert_called_with(3)
...
>>> test()

spec, create và các đối số khác cho patch.object() có cùng ý nghĩa như đối với patch().

patch.dict

patch.dict(in_dict, values=(), clear=False, **kwargs)

Vá một từ điển hoặc đối tượng giống như từ điển và khôi phục từ điển về trạng thái ban đầu sau khi kiểm tra, trong đó từ điển được khôi phục là bản sao của từ điển như trước khi kiểm tra.

in_dict có thể là một từ điển hoặc một vùng chứa ánh xạ. Nếu đó là ánh xạ thì ít nhất nó phải hỗ trợ nhận, thiết lập và xóa các mục cộng với việc lặp lại các phím.

in_dict cũng có thể là một chuỗi chỉ định tên của từ điển, sau đó sẽ được tìm nạp bằng cách nhập nó.

values có thể là một từ điển chứa các giá trị cần đặt trong từ điển. values cũng có thể là một cặp (key, value) có thể lặp lại.

Nếu clear đúng thì từ điển sẽ bị xóa trước khi các giá trị mới được đặt.

patch.dict() cũng có thể được gọi với các đối số từ khóa tùy ý để đặt giá trị trong từ điển.

Thay đổi trong phiên bản 3.8: patch.dict() hiện trả về từ điển đã vá khi được sử dụng làm trình quản lý ngữ cảnh.

patch.dict() có thể được sử dụng làm trình quản lý bối cảnh, trình trang trí hoặc trình trang trí lớp:

>>> foo = {}
>>> @patch.dict(foo, {'newkey': 'newvalue'})
... def test():
...     assert foo == {'newkey': 'newvalue'}
...
>>> test()
>>> assert foo == {}

Khi được sử dụng làm công cụ trang trí lớp, patch.dict() vinh danh patch.TEST_PREFIX (mặc định là 'test') vì đã chọn phương thức nào để bọc:

>>> import os
>>> import unittest
>>> from unittest.mock import patch
>>> @patch.dict('os.environ', {'newkey': 'newvalue'})
... class TestSample(unittest.TestCase):
...     def test_sample(self):
...         self.assertEqual(os.environ['newkey'], 'newvalue')

Nếu bạn muốn sử dụng tiền tố khác cho thử nghiệm của mình, bạn có thể thông báo cho người vá lỗi về tiền tố khác bằng cách đặt patch.TEST_PREFIX. Để biết thêm chi tiết về cách thay đổi giá trị, hãy xem TEST_PREFIX.

patch.dict() có thể được sử dụng để thêm thành viên vào từ điển hoặc đơn giản là để bài kiểm tra thay đổi từ điển và đảm bảo từ điển được khôi phục khi quá trình kiểm tra kết thúc.

>>> foo = {}
>>> with patch.dict(foo, {'newkey': 'newvalue'}) as patched_foo:
...     assert foo == {'newkey': 'newvalue'}
...     assert patched_foo == {'newkey': 'newvalue'}
...     # You can add, update or delete keys of foo (or patched_foo, it's the same dict)
...     patched_foo['spam'] = 'eggs'
...
>>> assert foo == {}
>>> assert patched_foo == {}
>>> import os
>>> with patch.dict('os.environ', {'newkey': 'newvalue'}):
...     print(os.environ['newkey'])
...
newvalue
>>> assert 'newkey' not in os.environ

Từ khóa có thể được sử dụng trong lệnh gọi patch.dict() để đặt giá trị trong từ điển:

>>> mymodule = MagicMock()
>>> mymodule.function.return_value = 'fish'
>>> with patch.dict('sys.modules', mymodule=mymodule):
...     import mymodule
...     mymodule.function('some', 'args')
...
'fish'

patch.dict() có thể được sử dụng với từ điển giống như các đối tượng không thực sự là từ điển. Ở mức tối thiểu, họ phải hỗ trợ việc nhận, cài đặt, xóa mục và lặp lại hoặc kiểm tra tư cách thành viên. Điều này tương ứng với các phương thức ma thuật __getitem__(), __setitem__(), __delitem__()__iter__() hoặc __contains__().

>>> class Container:
...     def __init__(self):
...         self.values = {}
...     def __getitem__(self, name):
...         return self.values[name]
...     def __setitem__(self, name, value):
...         self.values[name] = value
...     def __delitem__(self, name):
...         del self.values[name]
...     def __iter__(self):
...         return iter(self.values)
...
>>> thing = Container()
>>> thing['one'] = 1
>>> with patch.dict(thing, one=2, two=3):
...     assert thing['one'] == 2
...     assert thing['two'] == 3
...
>>> assert thing['one'] == 1
>>> assert list(thing) == ['one']

patch.multiple

patch.multiple(target, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)

Thực hiện nhiều bản vá trong một cuộc gọi. Nó lấy đối tượng cần vá (dưới dạng đối tượng hoặc chuỗi để tìm nạp đối tượng bằng cách nhập) và đối số từ khóa cho các bản vá

với patch.multiple(settings, FIRST_PATCH='one', SECOND_PATCH='two'):
    ...

Sử dụng DEFAULT làm giá trị nếu bạn muốn patch.multiple() tạo mô hình cho bạn. Trong trường hợp này, các mô hình đã tạo sẽ được chuyển vào hàm trang trí theo từ khóa và từ điển được trả về khi patch.multiple() được sử dụng làm trình quản lý ngữ cảnh.

patch.multiple() có thể được sử dụng làm công cụ trang trí, trang trí lớp hoặc trình quản lý bối cảnh. Các đối số spec, spec_set, create, autospecnew_callable có cùng ý nghĩa như đối với patch(). Những đối số này sẽ được áp dụng cho các bản vá all do patch.multiple() thực hiện.

Khi được sử dụng làm công cụ trang trí lớp, patch.multiple() vinh danh patch.TEST_PREFIX vì đã chọn phương pháp nào để bọc.

Nếu bạn muốn patch.multiple() tạo mô hình cho mình thì bạn có thể sử dụng DEFAULT làm giá trị. Nếu bạn sử dụng patch.multiple() làm công cụ trang trí thì các mô hình đã tạo sẽ được chuyển vào chức năng trang trí theo từ khóa.

>>> điều = đối tượng()
>>> khác = đối tượng()

>>> @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(thing, other):
... khẳng định isinstance(thing, MagicMock)
... khẳng định isinstance(other, MagicMock)
...
>>> test_function()

patch.multiple() có thể được lồng với các trình trang trí patch khác, nhưng đặt các đối số được truyền bởi từ khóa after bất kỳ đối số tiêu chuẩn nào được tạo bởi patch():

>>> @patch('sys.exit')
... @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(mock_exit, other, thing):
... khẳng định 'khác' trong repr(other)
... khẳng định 'điều' trong repr(thing)
... khẳng định 'thoát' trong Repr(mock_exit)
...
>>> test_function()

Nếu patch.multiple() được sử dụng làm trình quản lý bối cảnh, giá trị được trình quản lý bối cảnh trả về là một từ điển trong đó các mô hình được tạo được khóa theo tên:

>>> với patch.multiple('__main__', thing=DEFAULT, other=DEFAULT) làm giá trị:
... khẳng định 'khác' trong Repr(values['other'])
... khẳng định 'điều' trong repr(values['thing'])
... khẳng định giá trị['thing']  thứ
... khẳng định giá trị['other']  khác
...

phương pháp vá: bắt đầu và dừng

Tất cả các bản vá đều có phương thức start()stop(). Những điều này giúp việc vá lỗi trong các phương thức setUp trở nên đơn giản hơn hoặc khi bạn muốn thực hiện nhiều bản vá mà không cần lồng các trang trí hoặc câu lệnh.

Để sử dụng chúng, hãy gọi patch(), patch.object() hoặc patch.dict() như bình thường và giữ tham chiếu đến đối tượng patcher được trả về. Sau đó, bạn có thể gọi start() để đặt bản vá vào vị trí và stop() để hoàn tác nó.

Nếu bạn đang sử dụng patch() để tạo một bản mô phỏng cho mình thì nó sẽ được trả về bằng lệnh gọi tới patcher.start.

>>> patcher = patch('package.module.ClassName')
>>> từ -đun nhập gói
>>> gốc = -đun.ClassName
>>> new_mock = patcher.start()
>>> khẳng định module.ClassName không phải  bản gốc
>>> khẳng định module.ClassName  new_mock
>>> patcher.stop()
>>> khẳng định -đun.ClassName  bản gốc
>>> khẳng định -đun.ClassName không phải  new_mock

Trường hợp sử dụng điển hình cho việc này có thể là thực hiện nhiều bản vá trong phương thức setUp của TestCase:

>>> lớp MyTest(unittest.TestCase):
... def setUp(self):
... self.patcher1 = patch('package.module.Class1')
... self.patcher2 = patch('package.module.Class2')
... self.MockClass1 = self.patcher1.start()
... self.MockClass2 = self.patcher2.start()
...
... def TearsDown(self):
... self.patcher1.stop()
... self.patcher2.stop()
...
... def test_something(self):
... khẳng định package.module.Class1  self.MockClass1
... khẳng định package.module.Class2  self.MockClass2
...
>>> MyTest('test_something').run()

Cảnh báo

Nếu bạn sử dụng kỹ thuật này, bạn phải đảm bảo rằng việc vá lỗi được "hoàn tác" bằng cách gọi stop. Điều này có thể phức tạp hơn bạn nghĩ, bởi vì nếu một ngoại lệ được đưa ra trong setUp thì tearDown sẽ không được gọi. unittest.TestCase.addCleanup() làm cho việc này trở nên dễ dàng hơn:

>>> lớp MyTest(unittest.TestCase):
... def setUp(self):
... patcher = patch('package.module.Class')
... self.MockClass = patcher.start()
... self.addCleanup(patcher.stop)
...
... def test_something(self):
... khẳng định package.module.Class  self.MockClass
...

Là một phần thưởng bổ sung, bạn không còn cần phải giữ tham chiếu đến đối tượng patcher nữa.

Cũng có thể dừng tất cả các bản vá đã được bắt đầu bằng cách sử dụng patch.stopall().

patch.stopall()

Dừng tất cả các bản vá đang hoạt động. Chỉ dừng các bản vá bắt đầu bằng start.

vá nội dung

Bạn có thể vá bất kỳ nội dung nào trong một mô-đun. Ví dụ sau đây về các bản vá được tích hợp sẵn trong ord():

>>> @patch('__main__.ord')
... bài kiểm tra chắc chắn(mock_ord):
... mock_ord.return_value = 101
... in(ord('c'))
...
>>> kiểm tra()
101

TEST_PREFIX

Tất cả các bản vá có thể được sử dụng làm công cụ trang trí lớp. Khi được sử dụng theo cách này, chúng bao bọc mọi phương thức kiểm tra trên lớp. Những người vá lỗi nhận ra các phương thức bắt đầu bằng 'test' là phương pháp thử nghiệm. Đây cũng giống như cách unittest.TestLoader tìm các phương pháp thử nghiệm theo mặc định.

Có thể bạn muốn sử dụng một tiền tố khác cho các bài kiểm tra của mình. Bạn có thể thông báo cho người vá lỗi về tiền tố khác nhau bằng cách đặt patch.TEST_PREFIX:

>>> patch.TEST_PREFIX = 'foo'
>>> giá trị = 3
>>>
>>> @patch('__main__.value', 'không phải ba')
... lớp Điều:
... def foo_one(tự):
... in(giá trị)
... def foo_two(tự):
... in(giá trị)
...
>>>
>>> Điều().foo_one()
không phải ba
>>> Thing().foo_two()
không phải ba
>>> giá trị
3

Trang trí bản vá lồng nhau

Nếu bạn muốn thực hiện nhiều bản vá thì bạn chỉ cần xếp chồng các trang trí lên nhau.

Bạn có thể xếp chồng nhiều công cụ trang trí bản vá bằng cách sử dụng mẫu này:

>>> @patch.object(SomeClass, 'class_method')
... @patch.object(SomeClass, 'static_method')
... def test(mock1, mock2):
...     assert SomeClass.static_method is mock1
...     assert SomeClass.class_method is mock2
...     SomeClass.static_method('foo')
...     SomeClass.class_method('bar')
...     return mock1, mock2
...
>>> mock1, mock2 = test()
>>> mock1.assert_called_once_with('foo')
>>> mock2.assert_called_once_with('bar')

Lưu ý rằng các trang trí được áp dụng từ dưới lên. Đây là cách tiêu chuẩn mà Python áp dụng các trình trang trí. Thứ tự của các bản mô phỏng được chuyển vào hàm kiểm tra của bạn khớp với thứ tự này.

vá ở đâu

patch() hoạt động bằng cách (tạm thời) thay đổi đối tượng mà name trỏ tới bằng một đối tượng khác. Có thể có nhiều tên trỏ đến bất kỳ đối tượng riêng lẻ nào, vì vậy để bản vá hoạt động, bạn phải đảm bảo rằng bạn vá tên được hệ thống đang thử nghiệm sử dụng.

Nguyên tắc cơ bản là bạn vá nơi một đối tượng là looked up, không nhất thiết phải ở cùng một nơi với nơi nó được xác định. Một vài ví dụ sẽ giúp làm rõ điều này.

Hãy tưởng tượng chúng ta có một dự án muốn thử nghiệm với cấu trúc sau:

a.py
    -> Định nghĩa SomeClass

b.py
    -> từ một SomeClass nhập khẩu
    -> some_function khởi tạo SomeClass

Bây giờ chúng tôi muốn kiểm tra some_function nhưng chúng tôi muốn mô phỏng SomeClass bằng patch(). Vấn đề là khi nhập mô-đun b, chúng ta sẽ phải làm gì khi nhập SomeClass từ mô-đun a. Nếu chúng tôi sử dụng patch() để mô phỏng a.SomeClass thì nó sẽ không ảnh hưởng gì đến thử nghiệm của chúng tôi; mô-đun b đã có tham chiếu đến real SomeClass và có vẻ như bản vá của chúng tôi không có tác dụng.

Điều quan trọng là vá SomeClass ở nơi nó được sử dụng (hoặc nơi nó được tra cứu). Trong trường hợp này, some_function sẽ thực sự tra cứu SomeClass trong mô-đun b, nơi chúng tôi đã nhập nó. Bản vá sẽ trông giống như:

@patch('b.SomeClass')

Tuy nhiên, hãy xem xét kịch bản thay thế trong đó thay vì mô-đun from a import SomeClass b, import asome_function sử dụng a.SomeClass. Cả hai hình thức nhập khẩu này đều phổ biến. Trong trường hợp này, lớp chúng tôi muốn vá đang được tra cứu trong mô-đun và vì vậy thay vào đó chúng tôi phải vá a.SomeClass

@patch('a.SomeClass')

Vá mô tả và đối tượng proxy

Cả patchpatch.object đều vá và khôi phục chính xác các bộ mô tả: phương thức lớp, phương thức tĩnh và thuộc tính. Bạn nên vá những thứ này trên class thay vì trên một phiên bản. Chúng cũng hoạt động với các đối tượng some có quyền truy cập thuộc tính proxy, như django settings object.

Hỗ trợ MagicMock và phương pháp ma thuật

Phương pháp ma thuật chế giễu

Mock hỗ trợ mô phỏng các phương thức giao thức Python, còn được gọi là "magic methods". Điều này cho phép các đối tượng mô phỏng thay thế các thùng chứa hoặc các đối tượng khác triển khai giao thức Python.

Bởi vì các phương thức ma thuật được tra cứu khác với các phương thức thông thường [2] nên sự hỗ trợ này đã được triển khai đặc biệt. Điều này có nghĩa là chỉ những phương pháp ma thuật cụ thể mới được hỗ trợ. Danh sách được hỗ trợ bao gồm tất cả almost. Nếu có bất kỳ thiếu sót nào bạn cần xin vui lòng cho chúng tôi biết.

Bạn mô phỏng các phương thức ma thuật bằng cách đặt phương thức mà bạn quan tâm vào một hàm hoặc một phiên bản mô phỏng. Nếu bạn đang sử dụng một hàm thì must sẽ lấy self làm đối số đầu tiên [3].

>>> def __str__(self):
...     return 'fooble'
...
>>> mock = Mock()
>>> mock.__str__ = __str__
>>> str(mock)
'fooble'
>>> mock = Mock()
>>> mock.__str__ = Mock()
>>> mock.__str__.return_value = 'fooble'
>>> str(mock)
'fooble'
>>> mock = Mock()
>>> mock.__iter__ = Mock(return_value=iter([]))
>>> list(mock)
[]

Một trường hợp sử dụng cho việc này là để mô phỏng các đối tượng được sử dụng làm trình quản lý bối cảnh trong câu lệnh with:

>>> mock = Mock()
>>> mock.__enter__ = Mock(return_value='foo')
>>> mock.__exit__ = Mock(return_value=False)
>>> with mock as m:
...     assert m == 'foo'
...
>>> mock.__enter__.assert_called_with()
>>> mock.__exit__.assert_called_with(None, None, None)

Các lệnh gọi đến phương thức ma thuật không xuất hiện trong method_calls nhưng chúng được ghi lại trong mock_calls.

Ghi chú

Nếu bạn sử dụng đối số từ khóa spec để tạo một bản mô phỏng thì việc cố gắng đặt một phương thức ma thuật không có trong thông số kỹ thuật sẽ tạo ra một AttributeError.

Danh sách đầy đủ các phương pháp ma thuật được hỗ trợ là:

  • __hash__, __sizeof__, __repr____str__

  • __dir__, __format____subclasses__

  • __round__, __floor__, __trunc____ceil__

  • So sánh: __lt__, __gt__, __le__, __ge__, __eq____ne__

  • Phương thức vùng chứa: __getitem__, __setitem__, __delitem__, __contains__, __len__, __iter__, __reversed____missing__

  • Trình quản lý bối cảnh: __enter__, __exit__, __aenter____aexit__

  • Phương thức số đơn nhất: __neg__, __pos____invert__

  • Các phương thức số (bao gồm các biến thể tay phải và tại chỗ): __add__, __sub__, __mul__, __matmul__, __truediv__, __floordiv__, __mod__, __divmod__, __lshift__, __rshift__, __and__, __xor__, __or____pow__

  • Phương thức chuyển đổi số: __complex__, __int__, __float____index__

  • Các phương thức mô tả: __get__, __set____delete__

  • Dưa chua: __reduce__, __reduce_ex__, __getinitargs__, __getnewargs__, __getstate____setstate__

  • Biểu diễn đường dẫn hệ thống tệp: __fspath__

  • Phương pháp lặp không đồng bộ: __aiter____anext__

Thay đổi trong phiên bản 3.8: Đã thêm hỗ trợ cho os.PathLike.__fspath__().

Thay đổi trong phiên bản 3.8: Đã thêm hỗ trợ cho __aenter__, __aexit__, __aiter____anext__.

Các phương thức sau tồn tại nhưng được not hỗ trợ vì chúng đang được mô hình sử dụng, không thể đặt động hoặc có thể gây ra sự cố:

  • __getattr__, __setattr__, __init____new__

  • __prepare__, __instancecheck__, __subclasscheck__, __del__

Mô phỏng ma thuật

Có hai biến thể MagicMock: MagicMockNonCallableMagicMock.

class unittest.mock.MagicMock(*args, **kw)

MagicMock là một lớp con của Mock với cách triển khai mặc định của hầu hết magic methods. Bạn có thể sử dụng MagicMock mà không cần phải tự mình cấu hình các phương thức ma thuật.

Các tham số của hàm tạo có ý nghĩa tương tự như đối với Mock.

Nếu bạn sử dụng các đối số spec hoặc spec_set thì các phương thức ma thuật only tồn tại trong thông số kỹ thuật sẽ được tạo.

class unittest.mock.NonCallableMagicMock(*args, **kw)

Một phiên bản không thể gọi được của MagicMock.

Các tham số của hàm tạo có cùng ý nghĩa như đối với MagicMock, ngoại trừ return_valueside_effect không có ý nghĩa gì trên một mô hình không thể gọi được.

Các phương thức ma thuật được thiết lập với các đối tượng MagicMock, vì vậy bạn có thể định cấu hình và sử dụng chúng theo cách thông thường:

>>> mock = MagicMock()
>>> mock[3] = 'fish'
>>> mock.__setitem__.assert_called_with(3, 'fish')
>>> mock.__getitem__.return_value = 'result'
>>> mock[2]
'result'

Theo mặc định, nhiều phương thức giao thức được yêu cầu để trả về các đối tượng thuộc một loại cụ thể. Các phương thức này được cấu hình sẵn với giá trị trả về mặc định để bạn có thể sử dụng chúng mà không cần phải làm gì nếu không quan tâm đến giá trị trả về. Bạn vẫn có thể set giá trị trả về theo cách thủ công nếu muốn thay đổi giá trị mặc định.

Các phương thức và giá trị mặc định của chúng:

  • __lt__: NotImplemented

  • __gt__: NotImplemented

  • __le__: NotImplemented

  • __ge__: NotImplemented

  • __int__: 1

  • __contains__: False

  • __len__: 0

  • __iter__: iter([])

  • __exit__: False

  • __aexit__: False

  • __complex__: 1j

  • __float__: 1.0

  • __bool__: True

  • __index__: 1

  • __hash__: hàm băm mặc định cho bản mô phỏng

  • __str__: str mặc định cho bản mô phỏng

  • __sizeof__: sizeof mặc định cho mô hình

Ví dụ:

>>> mock = MagicMock()
>>> int(mock)
1
>>> len(mock)
0
>>> list(mock)
[]
>>> object() in mock
False

Hai phương thức bình đẳng, __eq__()__ne__(), đều đặc biệt. Họ thực hiện so sánh bình đẳng mặc định về danh tính, sử dụng thuộc tính side_effect, trừ khi bạn thay đổi giá trị trả về của chúng để trả về giá trị khác

>>> MagicMock() == 3
sai
>>> MagicMock() != 3
đúng
>>> giả = MagicMock()
>>> mock.__eq__.return_value = Đúng
>>> giả == 3
đúng

Giá trị trả về của MagicMock.__iter__() có thể là bất kỳ đối tượng có thể lặp nào và không bắt buộc phải là một trình vòng lặp:

>>> mock = MagicMock()
>>> mock.__iter__.return_value = ['a', 'b', 'c']
>>> list(mock)
['a', 'b', 'c']
>>> list(mock)
['a', 'b', 'c']

Nếu giá trị trả về is là một trình vòng lặp, thì việc lặp lại nó một lần sẽ tiêu tốn nó và các lần lặp tiếp theo sẽ dẫn đến một danh sách trống:

>>> mock.__iter__.return_value = iter(['a', 'b', 'c'])
>>> list(mock)
['a', 'b', 'c']
>>> list(mock)
[]

MagicMock có tất cả các phương thức ma thuật được hỗ trợ được định cấu hình ngoại trừ một số phương thức ít người biết đến và lỗi thời. Bạn vẫn có thể thiết lập những thứ này nếu muốn.

Các phương thức ma thuật được hỗ trợ nhưng không được thiết lập theo mặc định trong MagicMock là:

  • __subclasses__

  • __dir__

  • __format__

  • __get__, __set____delete__

  • __reversed____missing__

  • __reduce__, __reduce_ex__, __getinitargs__, __getnewargs__, __getstate____setstate__

  • __getformat__

Người trợ giúp

lính gác

unittest.mock.sentinel

Đối tượng sentinel cung cấp một cách thuận tiện để cung cấp các đối tượng duy nhất cho các thử nghiệm của bạn.

Các thuộc tính được tạo theo yêu cầu khi bạn truy cập chúng theo tên. Truy cập cùng một thuộc tính sẽ luôn trả về cùng một đối tượng. Các đối tượng được trả về có giá trị lặp lại hợp lý để có thể đọc được các thông báo lỗi kiểm tra.

Thay đổi trong phiên bản 3.7: Các thuộc tính sentinel hiện giữ nguyên danh tính của chúng khi chúng là copied hoặc pickled.

Đôi khi, khi kiểm tra, bạn cần kiểm tra xem một đối tượng cụ thể có được truyền dưới dạng đối số cho một phương thức khác hay được trả về hay không. Có thể phổ biến việc tạo các đối tượng trọng điểm được đặt tên để kiểm tra điều này. sentinel cung cấp một cách thuận tiện để tạo và kiểm tra danh tính của các đối tượng như thế này.

Trong ví dụ này, chúng tôi vá method để trả về sentinel.some_object:

>>> real = ProductionClass()
>>> real.method = Mock(name="method")
>>> real.method.return_value = sentinel.some_object
>>> result = real.method()
>>> assert result is sentinel.some_object
>>> result
sentinel.some_object

DEFAULT

unittest.mock.DEFAULT

Đối tượng DEFAULT là một trọng điểm được tạo trước (thực ra là sentinel.DEFAULT). Nó có thể được sử dụng bởi các hàm side_effect để chỉ ra rằng nên sử dụng giá trị trả về bình thường.

gọi

unittest.mock.call(*args, **kwargs)

call() là một đối tượng trợ giúp để đưa ra các xác nhận đơn giản hơn, nhằm so sánh với call_args, call_args_list, mock_callsmethod_calls. call() cũng có thể được sử dụng với assert_has_calls().

>>> m = MagicMock(return_value=None)
>>> m(1, 2, a='foo', b='bar')
>>> m()
>>> m.call_args_list == [call(1, 2, a='foo', b='bar'), call()]
True
call.call_list()

Đối với đối tượng cuộc gọi đại diện cho nhiều cuộc gọi, call_list() trả về danh sách tất cả các cuộc gọi trung gian cũng như cuộc gọi cuối cùng.

call_list đặc biệt hữu ích để đưa ra các xác nhận về "các cuộc gọi nối tiếp". Cuộc gọi theo chuỗi là nhiều cuộc gọi trên một dòng mã. Điều này dẫn đến nhiều mục trong mock_calls trên một bản mô phỏng. Việc xây dựng chuỗi cuộc gọi theo cách thủ công có thể rất tẻ nhạt.

call_list() có thể xây dựng chuỗi cuộc gọi từ cùng một cuộc gọi theo chuỗi:

>>> m = MagicMock()
>>> m(1).method(arg='foo').other('bar')(2.0)
<MagicMock name='mock().method().other()()' id='...'>
>>> kall = call(1).method(arg='foo').other('bar')(2.0)
>>> kall.call_list()
[call(1),
 call().method(arg='foo'),
 call().method().other('bar'),
 call().method().other()(2.0)]
>>> m.mock_calls == kall.call_list()
True

Đối tượng call là một bộ gồm (đối số vị trí, đối số từ khóa) hoặc (tên, đối số vị trí, đối số từ khóa) tùy thuộc vào cách nó được xây dựng. Khi bạn tự xây dựng chúng, điều này không đặc biệt thú vị, nhưng các đối tượng call nằm trong các thuộc tính Mock.call_args, Mock.call_args_listMock.mock_calls có thể được xem xét nội tâm để có được các đối số riêng lẻ mà chúng chứa.

Các đối tượng call trong Mock.call_argsMock.call_args_list là hai bộ dữ liệu (đối số vị trí, đối số từ khóa) trong khi các đối tượng call trong Mock.mock_calls, cùng với các đối tượng bạn tự tạo, là ba bộ dữ liệu (tên, đối số vị trí, đối số từ khóa).

Bạn có thể sử dụng "tupleness" của chúng để rút ra các đối số riêng lẻ để xem xét nội tâm và khẳng định phức tạp hơn. Các đối số vị trí là một bộ (một bộ trống nếu không có đối số vị trí) và các đối số từ khóa là một từ điển:

>>> m = MagicMock(return_value=None)
>>> m(1, 2, 3, arg='one', arg2='two')
>>> kall = m.call_args
>>> kall.args
(1, 2, 3)
>>> kall.kwargs
{'arg': 'one', 'arg2': 'two'}
>>> kall.args is kall[0]
True
>>> kall.kwargs is kall[1]
True
>>> m = MagicMock()
>>> m.foo(4, 5, 6, arg='two', arg2='three')
<MagicMock name='mock.foo()' id='...'>
>>> kall = m.mock_calls[0]
>>> name, args, kwargs = kall
>>> name
'foo'
>>> args
(4, 5, 6)
>>> kwargs
{'arg': 'two', 'arg2': 'three'}
>>> name is m.mock_calls[0][0]
True

tạo_autospec

unittest.mock.create_autospec(spec, spec_set=False, instance=False, **kwargs)

Tạo một đối tượng giả bằng cách sử dụng một đối tượng khác làm thông số kỹ thuật. Các thuộc tính trên mô hình sẽ sử dụng thuộc tính tương ứng trên đối tượng spec làm thông số kỹ thuật của chúng.

Các hàm hoặc phương thức được mô phỏng sẽ được kiểm tra các đối số của chúng để đảm bảo rằng chúng được gọi với chữ ký chính xác.

Nếu spec_setTrue thì việc cố gắng đặt các thuộc tính không tồn tại trên đối tượng spec sẽ tạo ra AttributeError.

Nếu một lớp được sử dụng làm thông số kỹ thuật thì giá trị trả về của mô hình (thể hiện của lớp) sẽ có cùng thông số kỹ thuật. Bạn có thể sử dụng một lớp làm thông số kỹ thuật cho một đối tượng phiên bản bằng cách chuyển instance=True. Bản mô phỏng được trả về sẽ chỉ có thể gọi được nếu các phiên bản của mô hình đó có thể gọi được.

create_autospec() cũng nhận các đối số từ khóa tùy ý được chuyển đến hàm tạo của mô hình đã tạo.

Xem Tự động xác định để biết ví dụ về cách sử dụng tính năng tự động xác định với create_autospec() và đối số autospec cho patch().

Thay đổi trong phiên bản 3.8: create_autospec() hiện trả về AsyncMock nếu mục tiêu là hàm không đồng bộ.

ANY

unittest.mock.ANY

Đôi khi, bạn có thể cần đưa ra các xác nhận về some của các đối số trong lệnh gọi mô phỏng, nhưng không quan tâm đến một số đối số hoặc muốn tách chúng ra khỏi call_args và đưa ra các xác nhận phức tạp hơn về chúng.

Để bỏ qua một số đối số nhất định, bạn có thể chuyển vào các đối tượng so sánh bằng everything. Các lệnh gọi tới assert_called_with()assert_called_once_with() sau đó sẽ thành công bất kể nội dung nào được truyền vào.

>>> mock = Mock(return_value=None)
>>> mock('foo', bar=object())
>>> mock.assert_called_once_with('foo', bar=ANY)

ANY cũng có thể được sử dụng để so sánh với danh sách cuộc gọi như mock_calls:

>>> m = MagicMock(return_value=None)
>>> m(1)
>>> m(1, 2)
>>> m(object())
>>> m.mock_calls == [call(1), call(1, 2), ANY]
True

ANY không bị giới hạn trong việc so sánh với các đối tượng cuộc gọi và do đó cũng có thể được sử dụng trong các xác nhận kiểm tra

lớp TestStringMethods(unittest.TestCase):

    def test_split(tự):
        s = 'xin chào thế giới'
        self.assertEqual(s.split(), ['xin chào', ANY])

FILTER_DIR

unittest.mock.FILTER_DIR

FILTER_DIR là một biến cấp mô-đun kiểm soát cách các đối tượng mô phỏng phản hồi với dir(). Mặc định là True, sử dụng bộ lọc được mô tả bên dưới để chỉ hiển thị các thành viên hữu ích. Nếu bạn không thích tính năng lọc này hoặc cần tắt tính năng lọc này cho mục đích chẩn đoán, hãy đặt mock.FILTER_DIR = False.

Khi bật tính năng lọc, dir(some_mock) chỉ hiển thị các thuộc tính hữu ích và sẽ bao gồm mọi thuộc tính được tạo động mà thông thường không được hiển thị. Nếu mô hình được tạo bằng spec (hoặc tất nhiên là autospec) thì tất cả các thuộc tính từ bản gốc sẽ được hiển thị, ngay cả khi chúng chưa được truy cập:

>>> thư mục(Mock())
['assert_any_call',
 'khẳng định_được gọi',
 'khẳng định_gọi_một lần',
 'khẳng định_gọi_once_with',
 'khẳng định_gọi_với',
 'assert_has_calls',
 'assert_not_call',
 'đính kèm_mock',
 ...
>>> từ yêu cầu nhập urllib
>>> dir(Mock(spec=request))
['AbstractBasicAuthHandler',
 'AbstractDigestAuthHandler',
 'Tóm tắtHTTPHandler',
 'BaseHandler',
 ...

Nhiều thuộc tính tiền tố gạch dưới và dấu gạch dưới kép không hữu ích lắm (riêng tư đối với Mock chứ không phải thứ bị chế giễu) đã được lọc khỏi kết quả của việc gọi dir() trên Mock. Nếu bạn không thích hành vi này, bạn có thể tắt nó bằng cách đặt công tắc cấp độ mô-đun FILTER_DIR:

>>> từ  hình nhập khẩu nhỏ nhất
>>> mock.FILTER_DIR = Sai
>>> thư mục(mock.Mock())
['_NonCallableMock__get_return_value',
 '_NonCallableMock__get_side_effect',
 '_NonCallableMock__return_value_doc',
 '_NonCallableMock__set_return_value',
 '_NonCallableMock__set_side_effect',
 '__gọi__',
 '__lớp__',
 ...

Ngoài ra, bạn chỉ có thể sử dụng vars(my_mock) (thành viên phiên bản) và dir(type(my_mock)) (loại thành viên) để bỏ qua quá trình lọc bất kể FILTER_DIR.

mock_open

unittest.mock.mock_open(mock=None, read_data=None)

Một hàm trợ giúp để tạo một bản mô phỏng thay thế việc sử dụng open(). Nó hoạt động cho open() được gọi trực tiếp hoặc được sử dụng làm trình quản lý bối cảnh.

Đối số mock là đối tượng mô phỏng để định cấu hình. Nếu None (mặc định) thì MagicMock sẽ được tạo cho bạn, với API giới hạn ở các phương thức hoặc thuộc tính có sẵn trên các thẻ điều khiển tệp tiêu chuẩn.

read_data là một chuỗi để trả về các phương thức read(), readline()readlines() của trình xử lý tệp. Các cuộc gọi đến các phương thức đó sẽ lấy dữ liệu từ read_data cho đến khi cạn kiệt. Bản mô phỏng của các phương pháp này khá đơn giản: mỗi khi mock được gọi, read_data sẽ được tua lại về điểm bắt đầu. Nếu bạn cần kiểm soát nhiều hơn dữ liệu mà bạn đang cung cấp cho mã đã kiểm tra, bạn sẽ cần phải tùy chỉnh mô hình này cho chính mình. Khi điều đó vẫn chưa đủ, một trong các gói hệ thống tệp trong bộ nhớ trên PyPI có thể cung cấp hệ thống tệp thực tế để thử nghiệm.

Thay đổi trong phiên bản 3.4: Đã thêm hỗ trợ readline()readlines(). Mô hình read() đã thay đổi để tiêu thụ read_data thay vì trả lại nó trong mỗi cuộc gọi.

Thay đổi trong phiên bản 3.5: read_data hiện được đặt lại trên mỗi cuộc gọi tới mock.

Thay đổi trong phiên bản 3.8: Đã thêm __iter__() vào quá trình triển khai để phép lặp (chẳng hạn như trong vòng lặp for) tiêu thụ read_data một cách chính xác.

Sử dụng open() làm trình quản lý bối cảnh là một cách tuyệt vời để đảm bảo việc xử lý tệp của bạn được đóng đúng cách và đang trở nên phổ biến

với open('/some/path', 'w')  f:
    f.write('cái gì đó')

Vấn đề là ngay cả khi bạn thực hiện cuộc gọi tới open() thì returned object vẫn được sử dụng làm trình quản lý bối cảnh (và có __enter__()__exit__() được gọi).

Việc mô phỏng các trình quản lý bối cảnh bằng MagicMock là đủ phổ biến và đủ khó để chức năng trợ giúp trở nên hữu ích.

>>> m = mock_open()
>>> với bản ('__main__.open', m):
... với open('foo', 'w')  h:
... h.write('một số thứ')
...
>>> m.mock_calls
[gọi('foo', 'w'),
 gọi().__enter__(),
 call().write('một số thứ'),
 call().__exit__(Không, Không, Không)]
>>> m.assert_called_once_with('foo', 'w')
>>> xử  = m()
>>> hand.write.assert_gọi_once_with('một số thứ')

Và để đọc tập tin

>>> với patch('__main__.open', mock_open(read_data='bibble'))  m:
... với open('foo')  h:
... kết quả = h.read()
...
>>> m.assert_called_once_with('foo')
>>> khẳng định kết quả == 'bibble'

Tự động xác định

Tự động xác định dựa trên tính năng spec hiện có của mô hình. Nó giới hạn api của mô phỏng ở mức api của một đối tượng ban đầu (thông số kỹ thuật), nhưng nó có tính đệ quy (được triển khai một cách lười biếng) để các thuộc tính của mô phỏng chỉ có api giống như các thuộc tính của thông số kỹ thuật. Ngoài ra, các hàm/phương thức mô phỏng có cùng chữ ký cuộc gọi như bản gốc nên chúng sẽ đưa ra TypeError nếu chúng được gọi không chính xác.

Trước khi tôi giải thích cách hoạt động của tính năng tự động xác định, đây là lý do tại sao nó lại cần thiết.

Mock là một đối tượng rất mạnh mẽ và linh hoạt, nhưng nó có một lỗ hổng chung là chế nhạo. Nếu bạn cấu trúc lại một số mã của mình, đổi tên thành viên, v.v., mọi thử nghiệm về mã vẫn đang sử dụng old api nhưng sử dụng mô hình thay vì đối tượng thực sẽ vẫn vượt qua. Điều này có nghĩa là tất cả các bài kiểm tra của bạn đều có thể vượt qua ngay cả khi mã của bạn bị hỏng.

Thay đổi trong phiên bản 3.5: Trước 3.5, các bài kiểm tra có lỗi đánh máy trong từ khẳng định sẽ âm thầm vượt qua khi chúng phát sinh lỗi. Bạn vẫn có thể đạt được hành vi này bằng cách chuyển unsafe=True tới Mock.

Lưu ý rằng đây là một lý do khác tại sao bạn cần kiểm thử tích hợp cũng như kiểm thử đơn vị. Việc kiểm tra mọi thứ một cách riêng biệt đều ổn và tuyệt vời, nhưng nếu bạn không kiểm tra xem các thiết bị của mình được "kết nối với nhau" như thế nào thì vẫn còn rất nhiều chỗ cho các lỗi mà các bộ kiểm tra có thể đã phát hiện ra.

unittest.mock đã cung cấp một tính năng để trợ giúp việc này, được gọi là thông số kỹ thuật. Nếu bạn sử dụng một lớp hoặc phiên bản làm spec cho một bản mô phỏng thì bạn chỉ có thể truy cập các thuộc tính trên mô hình tồn tại trên lớp thực:

>>> from urllib import request
>>> mock = Mock(spec=request.Request)
>>> mock.assret_called_with  # Intentional typo!
Traceback (most recent call last):
 ...
AttributeError: Mock object has no attribute 'assret_called_with'

Thông số kỹ thuật chỉ áp dụng cho chính bản mô phỏng, vì vậy chúng tôi vẫn gặp vấn đề tương tự với bất kỳ phương pháp nào trên mô hình:

>>> mock.header_items()
<đối tượng mock.Mock ở 0x...>
>>> mock.header_items.assret_called_with() lỗi đánh máy # Intentional!

Tự động xác định giải quyết vấn đề này. Bạn có thể chuyển autospec=True sang patch()/patch.object() hoặc sử dụng hàm create_autospec() để tạo mô hình có thông số kỹ thuật. Nếu bạn sử dụng đối số autospec=True cho patch() thì đối tượng đang được thay thế sẽ được sử dụng làm đối tượng spec. Vì việc xác định được thực hiện một cách "một cách lười biếng" (thông số kỹ thuật được tạo khi các thuộc tính trên mô hình được truy cập), nên bạn có thể sử dụng nó với các đối tượng rất phức tạp hoặc được lồng sâu (như các mô-đun nhập mô-đun nhập mô-đun) mà không ảnh hưởng lớn đến hiệu suất.

Đây là một ví dụ về nó được sử dụng:

>>> từ yêu cầu nhập urllib
>>> patcher = patch('__main__.request', autospec=True)
>>> mock_request = patcher.start()
>>> yêu cầu  mock_request
đúng
>>> mock_request.Request
<MagicMock name='request.Request' spec='Request' id='...'>

Bạn có thể thấy request.Request có thông số kỹ thuật. request.Request nhận hai đối số trong hàm tạo (một trong số đó là self). Đây là điều sẽ xảy ra nếu chúng ta cố gắng gọi nó không chính xác:

>>> req = request.Request()
Traceback (cuộc gọi gần đây nhất):
 ...
TypeError: <lambda>() nhận ít nhất 2 đối số (1 đối số đã cho)

Thông số kỹ thuật này cũng áp dụng cho các lớp được khởi tạo (tức là giá trị trả về của các mô hình được chỉ định):

>>> req = request.Request('foo')
>>> yêu cầu
<NonCallableMagicMock name='request.Request()' spec='Request' id='...'>

Các đối tượng Request không thể gọi được, vì vậy giá trị trả về của việc khởi tạo request.Request bị mô phỏng của chúng tôi là một mô hình không thể gọi được. Với thông số kỹ thuật đã có, bất kỳ lỗi chính tả nào trong các xác nhận của chúng tôi sẽ gây ra lỗi chính xác

>>> req.add_header('thư rác', 'trứng')
<MagicMock name='request.Request().add_header()' id='...'>
>>> req.add_header.assret_called_với lỗi đánh máy # Intentional!
Traceback (cuộc gọi gần đây nhất):
 ...
AttributionError: Đối tượng giả không có thuộc tính 'assret_called_with'
>>> req.add_header.assert_gọi_with('spam', 'trứng')

Trong nhiều trường hợp, bạn chỉ có thể thêm autospec=True vào lệnh gọi patch() hiện có của mình và sau đó được bảo vệ khỏi lỗi do lỗi chính tả và thay đổi api.

Ngoài việc sử dụng autospec đến patch(), còn có create_autospec() để tạo trực tiếp các mô hình được xác định tự động:

>>> from urllib import request
>>> mock_request = create_autospec(request)
>>> mock_request.Request('foo', 'bar')
<NonCallableMagicMock name='mock.Request()' spec='Request' id='...'>

Tuy nhiên, điều này không phải là không có những hạn chế và hạn chế, đó là lý do tại sao nó không phải là hành vi mặc định. Để biết những thuộc tính nào có sẵn trên đối tượng thông số kỹ thuật, autospec phải xem xét kỹ (thuộc tính truy cập) thông số kỹ thuật đó. Khi bạn duyệt qua các thuộc tính trên mô hình, quá trình duyệt tương ứng của đối tượng ban đầu sẽ diễn ra bên dưới. Nếu bất kỳ đối tượng được chỉ định nào của bạn có thuộc tính hoặc bộ mô tả có thể kích hoạt việc thực thi mã thì bạn có thể không sử dụng được tính năng tự động xác định. Mặt khác, sẽ tốt hơn nhiều nếu bạn thiết kế các đối tượng của mình sao cho việc xem xét nội tâm được an toàn [4].

Một vấn đề nghiêm trọng hơn là các thuộc tính cá thể thường được tạo trong phương thức __init__() và hoàn toàn không tồn tại trên lớp. autospec không thể biết về bất kỳ thuộc tính nào được tạo động và hạn chế api ở các thuộc tính hiển thị.

>>> lớp Cái  đó:
... def __init__(self):
... tự.a = 33
...
>>> với bản ('__main__.Something', autospec=True):
... thứ = Cái  đó()
... điều.a
...
Traceback (cuộc gọi gần đây nhất):
  ...
AttributionError: Đối tượng giả không có thuộc tính 'a'

Có một số cách khác nhau để giải quyết vấn đề này. Cách dễ nhất nhưng không nhất thiết là ít khó chịu nhất là chỉ cần đặt các thuộc tính bắt buộc trên mô hình sau khi tạo. Chỉ vì autospec không cho phép bạn tìm nạp các thuộc tính không tồn tại trên thông số kỹ thuật, điều đó không ngăn cản bạn thiết lập chúng

>>> với bản ('__main__.Something', autospec=True):
... thứ = Cái  đó()
... điều.a = 33
...

Có một phiên bản mạnh mẽ hơn của cả specautospecdoes ngăn cản bạn thiết lập các thuộc tính không tồn tại. Điều này rất hữu ích nếu bạn muốn đảm bảo mã của mình chỉ có các thuộc tính hợp lệ sets, nhưng rõ ràng nó ngăn chặn tình huống cụ thể này:

>>> with patch('__main__.Something', autospec=True, spec_set=True):
...   thing = Something()
...   thing.a = 33
...
Traceback (most recent call last):
 ...
AttributeError: Mock object has no attribute 'a'

Có lẽ cách tốt nhất để giải quyết vấn đề là thêm các thuộc tính lớp làm giá trị mặc định cho các thành viên phiên bản được khởi tạo trong __init__(). Lưu ý rằng nếu bạn chỉ đặt các thuộc tính mặc định trong __init__() thì việc cung cấp chúng thông qua các thuộc tính lớp (tất nhiên được chia sẻ giữa các phiên bản) cũng nhanh hơn. ví dụ.

lớp Một cái  đó:
    một = 33

Điều này đưa ra một vấn đề khác. Việc cung cấp giá trị mặc định là None cho các thành viên mà sau này sẽ là một đối tượng thuộc loại khác là tương đối phổ biến. None sẽ là một thông số vô dụng vì nó không cho phép bạn truy cập các thuộc tính hoặc phương thức any trên đó. Vì Nonenever sẽ hữu ích như một thông số kỹ thuật và có thể chỉ ra một thành viên thường thuộc loại khác, nên autospec không sử dụng thông số kỹ thuật cho các thành viên được đặt thành None. Đây sẽ chỉ là những bản giả thông thường (à - MagicMocks):

>>> class Something:
...     member = None
...
>>> mock = create_autospec(Something)
>>> mock.member.foo.bar.baz()
<MagicMock name='mock.member.foo.bar.baz()' id='...'>

Nếu việc sửa đổi các lớp sản xuất của bạn để thêm các giá trị mặc định không theo ý thích của bạn thì sẽ có nhiều tùy chọn hơn. Một trong những cách này chỉ đơn giản là sử dụng một thể hiện làm thông số kỹ thuật thay vì lớp. Cách khác là tạo một lớp con của lớp sản xuất và thêm các giá trị mặc định cho lớp con mà không ảnh hưởng đến lớp sản xuất. Cả hai điều này đều yêu cầu bạn sử dụng một đối tượng thay thế làm thông số kỹ thuật. Rất may patch() hỗ trợ điều này - bạn có thể chỉ cần chuyển đối tượng thay thế làm đối số autospec

>>> lớp Cái  đó:
... def __init__(self):
... tự.a = 33
...
>>> lớp SomethingForTest(Something):
... a = 33
...
>>> p = patch('__main__.Something', autospec=SomethingForTest)
>>> mock = p.start()
>>> giả.a
<NonCallableMagicMock name='Something.a' spec='int' id='...'>

Niêm phong giả

unittest.mock.seal(mock)

Seal sẽ vô hiệu hóa việc tự động tạo các mô hình khi truy cập vào một thuộc tính của mô hình được niêm phong hoặc bất kỳ thuộc tính nào của nó đã được mô phỏng theo cách đệ quy.

Nếu một phiên bản mô phỏng có tên hoặc thông số kỹ thuật được gán cho một thuộc tính thì nó sẽ không được xem xét trong chuỗi niêm phong. Điều này cho phép người ta ngăn không cho seal cố định một phần của đối tượng giả.

>>>  phỏng =  phỏng()
>>> mock.submock.attribute1 = 2
>>> mock.not_submock = mock.Mock(name="sample_name")
>>> con dấu (giả)
>>> mock.new_attribute # This sẽ gây ra AttributionError.
>>> mock.submock.attribute2 # This sẽ gây ra AttributionError.
>>> mock.not_submock.attribute2 # This sẽ không tăng.

Added in version 3.7.

Thứ tự ưu tiên của side_effect, return_valuewraps

Thứ tự ưu tiên của chúng là:

  1. side_effect

  2. return_value

  3. wraps

Nếu cả ba đều được đặt, mô phỏng sẽ trả về giá trị từ side_effect, bỏ qua return_value và đối tượng được bao bọc hoàn toàn. Nếu bất kỳ hai được đặt, cái có mức độ ưu tiên cao hơn sẽ trả về giá trị. Bất kể thứ tự nào được đặt trước thì thứ tự ưu tiên vẫn không thay đổi.

>>> from unittest.mock import Mock
>>> class Order:
...     @staticmethod
...     def get_value():
...         return "third"
...
>>> order_mock = Mock(spec=Order, wraps=Order)
>>> order_mock.get_value.side_effect = ["first"]
>>> order_mock.get_value.return_value = "second"
>>> order_mock.get_value()
'first'

None là giá trị mặc định của side_effect, nếu bạn gán lại giá trị của nó cho None, thứ tự ưu tiên sẽ được kiểm tra giữa return_value và đối tượng được bao bọc, bỏ qua side_effect.

>>> order_mock.get_value.side_effect = None
>>> order_mock.get_value()
'second'

Nếu giá trị được side_effect trả về là DEFAULT thì giá trị đó sẽ bị bỏ qua và thứ tự ưu tiên sẽ chuyển sang giá trị kế tiếp để lấy giá trị trả về.

>>> from unittest.mock import DEFAULT
>>> order_mock.get_value.side_effect = [DEFAULT]
>>> order_mock.get_value()
'second'

Khi Mock bao bọc một đối tượng, giá trị mặc định của return_value sẽ là DEFAULT.

>>> order_mock = Mock(spec=Order, wraps=Order)
>>> order_mock.return_value
sentinel.DEFAULT
>>> order_mock.get_value.return_value
sentinel.DEFAULT

Thứ tự ưu tiên sẽ bỏ qua giá trị này và nó sẽ chuyển sang đối tượng kế tiếp cuối cùng là đối tượng được bao bọc.

Khi lệnh gọi thực đang được thực hiện đối với đối tượng được bao bọc, việc tạo một phiên bản của mô hình này sẽ trả về phiên bản thực của lớp. Các đối số vị trí, nếu có, được yêu cầu bởi đối tượng được bao bọc phải được thông qua.

>>> order_mock_instance = order_mock()
>>> isinstance(order_mock_instance, Order)
True
>>> order_mock_instance.get_value()
'third'
>>> order_mock.get_value.return_value = DEFAULT
>>> order_mock.get_value()
'third'
>>> order_mock.get_value.return_value = "second"
>>> order_mock.get_value()
'second'

Nhưng nếu bạn gán None cho nó, điều này sẽ không bị bỏ qua vì đây là một phép gán rõ ràng. Vì vậy, thứ tự ưu tiên sẽ không di chuyển đến đối tượng được bao bọc.

>>> order_mock.get_value.return_value = None
>>> order_mock.get_value() is None
True

Ngay cả khi bạn đặt cả ba cùng một lúc khi khởi tạo mô hình, thứ tự ưu tiên vẫn giữ nguyên:

>>> order_mock = Mock(spec=Order, wraps=Order,
...                   **{"get_value.side_effect": ["first"],
...                      "get_value.return_value": "second"}
...                   )
...
>>> order_mock.get_value()
'first'
>>> order_mock.get_value.side_effect = None
>>> order_mock.get_value()
'second'
>>> order_mock.get_value.return_value = DEFAULT
>>> order_mock.get_value()
'third'

Nếu side_effect cạn kiệt, thứ tự ưu tiên sẽ không nhận được giá trị từ những người kế nhiệm. Thay vào đó, ngoại lệ StopIteration được nêu ra.

>>> order_mock = Mock(spec=Order, wraps=Order)
>>> order_mock.get_value.side_effect = ["first side effect value",
...                                     "another side effect value"]
>>> order_mock.get_value.return_value = "second"
>>> order_mock.get_value()
'first side effect value'
>>> order_mock.get_value()
'another side effect value'
>>> order_mock.get_value()
Traceback (most recent call last):
 ...
StopIteration