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, MagicMock và patch().
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 Mock và MagicMock 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 vá nhập unittest.mock
>>> @patch('module.ClassName2')
... @patch('module.ClassName1')
... kiểm tra độ chính xác (MockClass1, MockClass2):
... mô-đun.ClassName1()
... mô-đun.ClassName2()
... khẳng định MockClass1 là module.ClassName1
... khẳng định MockClass2 là 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: NonCallableMock và NonCallableMagicMock
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
Mockmới.Mocknhậ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 traisinstance().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 quaunsafe=Truesẽ 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à
Nonethì 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 raAttributeError).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()và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ợpassert_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
Truesẽ đặt lạireturn_value:>>> mô phỏng = Mô 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
Truesẽ đặt lạiside_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ínhreturn_value,side_effecthoặ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_callsvàmock_callscủ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
Mockgiới hạn kết quả củadir(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_valuecũ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ềDEFAULTthì 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ý
DEFAULTgiố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_effectcó 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_effectthànhNonesẽ 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ínhargs, 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ínhkwargs, 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áchcall_args_list,method_callsvàmock_callslà các đối tượngcall. Đâ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
argsvàkwargs.
- 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
callcó 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ớicall_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_listlà các đối tượngcall. 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_callslà các đối tượngcall. 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_callsghi 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_callslà các đối tượngcall. 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 traisinstance()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 traisinstance()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ớiMock, ngoại trừ return_value và side_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
...
>>> mô phỏng = mô 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() và 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
propertyhoặc descriptor khác trên một lớp.PropertyMockcung cấp các phương thức__get__()và__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
PropertyMocktừ 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) là 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)
>>> gõ(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ượngAsyncMocksẽ 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_effecthoặcreturn_valuesau khi được chờ đợi:nếu
side_effectlà một hàm thì hàm async sẽ trả về kết quả của hàm đó,nếu
side_effectlà một ngoại lệ, hàm async sẽ đưa ra ngoại lệ đó,nếu
side_effectlà 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ả,StopAsyncIterationsẽ được nâng lên ngay lập tức,nếu
side_effectkhông được xác định, hàm async sẽ trả về giá trị được xác định bởireturn_value, do đó, theo mặc định, hàm async trả về một đối tượngAsyncMockmới.
Việc đặt spec của
MockhoặcMagicMockthà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,MagicMockhoặcAsyncMockthà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àAsyncMockhoặcMagicMock) hoặcMock(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 đặtawait_countthành 0,await_argsthành None và xóaawait_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ượngThreadingMockcung 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ố
timeouthoặc nếu không được đặt bởi thuộc tínhThreadingMock.DEFAULT_TIMEOUTthì 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
AssertionErrornế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
AssertionErrornế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_args và call_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_calls và mock_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) là child1:
... với patch('__main__.thing2', return_value=None) là 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.
vá¶
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
AsyncMocknếu đối tượng được vá là một hàm không đồng bộ hoặcMagicMocknếu không. Nếupatch()đượ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ếupatch()đượ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ọipatch(). 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 spec và spec_set được chuyển tới
MagicMocknếu bản vá đang tạo một đối số cho bạn.Ngoài ra, bạn có thể chuyển
spec=Truehoặcspec_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àMagicMockcho 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=Truethì 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 raTypeErrornế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ăngcreate_autospec()và Tự động xác định.Thay vì
autospec=True, bạn có thể chuyểnautospec=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àocreate=Truevà 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ằngpatch.TEST_PREFIX. Theo mặc định, đây là'test', phù hợp với cáchunittesttìm các bài kiểm tra. Bạn có thể chỉ định tiền tố thay thế bằng cách đặtpatch.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ớiAsyncMocknếu đối tượng được vá không đồng bộ, tớiMagicMocknếu không hoặc tới new_callable nếu được chỉ định.patch.dict(...),patch.multiple(...)và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 là SomeClass)
...
>>> chức năng(Không có)
đú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') là MockClass:
... instance = MockClass.return_value
... instance.method.return_value = 'foo'
... khẳng định Class() là phiên bản
... khẳng định Class().method() == 'foo'
...
Nếu bạn sử dụng spec hoặc spec_set và patch() đ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()
>>> ví 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) là mock_thing:
... khẳng định điều đó là 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_value và side_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()
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, autospec và new_callable có cùng ý nghĩa như đối vớipatch(). 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 danhpatch.TEST_PREFIXvì đã 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__() và __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
DEFAULTlàm giá trị nếu bạn muốnpatch.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ề khipatch.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, autospec và new_callable có cùng ý nghĩa như đối vớipatch(). Những đối số này sẽ được áp dụng cho các bản vá all dopatch.multiple()thực hiện.Khi được sử dụng làm công cụ trang trí lớp,
patch.multiple()vinh danhpatch.TEST_PREFIXvì đã 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'] là thứ
... khẳng định giá trị['other'] là 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() và 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ừ mô-đun nhập gói
>>> gốc = mô-đun.ClassName
>>> new_mock = patcher.start()
>>> khẳng định module.ClassName không phải là bản gốc
>>> khẳng định module.ClassName là new_mock
>>> patcher.stop()
>>> khẳng định mô-đun.ClassName là bản gốc
>>> khẳng định mô-đun.ClassName không phải là 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 là self.MockClass1
... khẳng định package.module.Class2 là 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 là 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 a và some_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ả patch và patch.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__và__str____dir__,__format__và__subclasses____round__,__floor__,__trunc__và__ceil__So sánh:
__lt__,__gt__,__le__,__ge__,__eq__và__ne__Phương thức vùng chứa:
__getitem__,__setitem__,__delitem__,__contains__,__len__,__iter__,__reversed__và__missing__Trình quản lý bối cảnh:
__enter__,__exit__,__aenter__và__aexit__Phương thức số đơn nhất:
__neg__,__pos__và__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__và__pow__Phương thức chuyển đổi số:
__complex__,__int__,__float__và__index__Các phương thức mô tả:
__get__,__set__và__delete__Dưa chua:
__reduce__,__reduce_ex__,__getinitargs__,__getnewargs__,__getstate__và__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__và__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__ và __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__và__new____prepare__,__instancecheck__,__subclasscheck__,__del__
Mô phỏng ma thuật¶
Có hai biến thể MagicMock: MagicMock và NonCallableMagicMock.
- class unittest.mock.MagicMock(*args, **kw)¶
MagicMocklà một lớp con củaMockvới cách triển khai mặc định của hầu hết magic methods. Bạn có thể sử dụngMagicMockmà 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_value và side_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__() và __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__và__delete____reversed__và__missing____reduce__,__reduce_ex__,__getinitargs__,__getnewargs__,__getstate__và__setstate____getformat__
Các phương thức ma thuật should được tra cứu trên lớp chứ không phải trên thể hiện. Các phiên bản Python khác nhau không nhất quán trong việc áp dụng quy tắc này. Các phương thức giao thức được hỗ trợ sẽ hoạt động với tất cả các phiên bản Python được hỗ trợ.
Về cơ bản, hàm này được nối với lớp, nhưng mỗi phiên bản Mock được giữ tách biệt với các phiên bản khác.
Người trợ giúp¶
lính gác¶
- unittest.mock.sentinel¶
Đối tượng
sentinelcung 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.
Đô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
DEFAULTlà 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àmside_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ớicall_args,call_args_list,mock_callsvàmethod_calls.call()cũng có thể được sử dụng vớiassert_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_list và Mock.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_args và Mock.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_set là
Truethì 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 raAttributeError.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() và 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ừ mô 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 choopen()đượ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ìMagicMocksẽ đượ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()và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()vàreadlines(). Mô hìnhread()đã 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') là 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__() và __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 vá('__main__.open', m):
... với open('foo', 'w') là 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ử lý = 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')) là m:
... với open('foo') là 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 là 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 gì đó:
... def __init__(self):
... tự.a = 33
...
>>> với bản vá('__main__.Something', autospec=True):
... thứ = Cái gì đó()
... đ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 vá('__main__.Something', autospec=True):
... thứ = Cái gì đó()
... điều.a = 33
...
Có một phiên bản mạnh mẽ hơn của cả spec và autospec mà does 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 gì đó:
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ì None là never 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 gì đó:
... 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='...'>
Điều này chỉ áp dụng cho các lớp hoặc các đối tượng đã được khởi tạo. Gọi một lớp giả để tạo một phiên bản giả does not tạo một phiên bản thực. Chỉ việc tra cứu thuộc tính - cùng với các lệnh gọi tới dir() - là xong.
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ả.
>>> mô phỏng = mô 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_value và wraps¶
Thứ tự ưu tiên của chúng là:
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'
Vì 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