Hướng dẫn python mock multiple get requests - python giả lập nhiều yêu cầu nhận

Tôi muốn có thể có nhiều cuộc gọi đến một hàm thuộc tính cụ thể trả về một kết quả khác nhau cho mỗi cuộc gọi liên tiếp.

Show

Trong ví dụ dưới đây, tôi muốn tăng lên 5 trong cuộc gọi đầu tiên và sau đó 10 trong cuộc gọi thứ hai.

Ex:

import mock

class A:
    def __init__(self):
        self.size = 0
    def increment(self, amount):
        self.size += amount
        return amount

@mock.patch("A.increment")
def test_method(self, mock_increment):
    def diff_inc(*args):
        def next_inc(*args):
            #I don't know what belongs in __some_obj__
            some_obj.side_effect = next_inc
            return 10
        return 5

    mock_increment.side_effect = diff_inc

Trang dưới đây có hầu hết mọi thứ mà tôi cần ngoại trừ việc nó cho rằng người gọi sẽ là một đối tượng có tên là "giả", nhưng điều này không thể được giả định.

http://mock.readthedocs.org/en/latest/examples.html#multiple-calls-with-different-effects

Hỏi ngày 18 tháng 4 năm 2014 lúc 17:40Apr 18, 2014 at 17:40

Hướng dẫn python mock multiple get requests - python giả lập nhiều yêu cầu nhận

FearlessfuturefearlessfutureFearlessFuture

2.1103 Huy hiệu vàng18 Huy hiệu bạc25 Huy hiệu Đồng3 gold badges18 silver badges25 bronze badges

1

Bạn chỉ có thể chuyển một hiệu ứng phụ cho phụ và có nó lặp lại thông qua danh sách các giá trị cho mỗi cuộc gọi bạn thực hiện.

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)

Đã trả lời ngày 21 tháng 4 năm 2014 lúc 23:31Apr 21, 2014 at 23:31

1

Tôi đã thử nghiệm và điều này sẽ hoạt động

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

Phiên bản

https://mock.readthedocs.io/en/latest/
mock>=2.0.0,<3.0

Đã trả lời ngày 23 tháng 10 năm 2020 lúc 12:25Oct 23, 2020 at 12:25

Hướng dẫn python mock multiple get requests - python giả lập nhiều yêu cầu nhận

Binh Hobinh hoBinh Ho

2.7821 Huy hiệu vàng23 Huy hiệu bạc27 Huy hiệu đồng1 gold badge23 silver badges27 bronze badges

Tôi nghĩ rằng các giá trị bật lên của một phương thức danh sách sẽ đơn giản hơn. Ví dụ dưới đây hoạt động cho bài kiểm tra bạn muốn thực hiện.

Ngoài ra, tôi đã có một thời gian khó khăn với thư viện giả trước đó và đã phát hiện ra rằng phương pháp

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
1 thường dễ sử dụng hơn.

import unittest
import mock


class A:
    def __init__(self):
        self.size = 0

    def increment(self, amount):
        self.size += amount
        return amount

incr_return_values = [5, 10]


def square_func(*args):
    return incr_return_values.pop(0)


class TestMock(unittest.TestCase):

    @mock.patch.object(A, 'increment')
    def test_mock(self, A):
        A.increment.side_effect = square_func

        self.assertEqual(A.increment(1), 5)
        self.assertEqual(A.increment(-20), 10)

Đã trả lời ngày 18 tháng 4 năm 2014 lúc 18:23Apr 18, 2014 at 18:23

Nate Brennandnate BrennandNate Brennand

1.4681 Huy hiệu vàng12 Huy hiệu bạc20 Huy hiệu Đồng1 gold badge12 silver badges20 bronze badges

1

Mới trong phiên bản 3.3.

Sử dụng Mock¶

Phương pháp vá lỗi

Sử dụng phổ biến cho các đối tượng

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
2 bao gồm:

  • Phương pháp vá lỗi

  • Phương thức ghi lại các cuộc gọi trên các đối tượng

Bạn có thể muốn thay thế một phương thức trên một đối tượng để kiểm tra xem nó được gọi bằng các đối số chính xác bởi một phần khác của hệ thống:

>>> real = SomeClass()
>>> real.method = MagicMock(name='method')
>>> real.method(3, 4, 5, key='value')

Khi giả của chúng tôi đã được sử dụng (

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
3 trong ví dụ này), nó có các phương thức và thuộc tính cho phép bạn đưa ra các xác nhận về cách sử dụng nó.

Ghi chú

Trong hầu hết các ví dụ này, các lớp

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
2 và
>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
5 có thể hoán đổi cho nhau. Vì
>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
5 là lớp có khả năng hơn, nó tạo ra một lớp hợp lý được sử dụng theo mặc định.

Khi giả đã được gọi là thuộc tính

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
7 của nó được đặt thành
>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
8. Quan trọng hơn, chúng ta có thể sử dụng phương pháp
>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
9 hoặc
>>> def some_function():
...     instance = module.Foo()
...     return instance.method()
...
>>> with patch('module.Foo') as mock:
...     instance = mock.return_value
...     instance.method.return_value = 'the result'
...     result = some_function()
...     assert result == 'the result'
0 để kiểm tra xem nó có được gọi với các đối số chính xác không.

Ví dụ này kiểm tra rằng việc gọi

>>> def some_function():
...     instance = module.Foo()
...     return instance.method()
...
>>> with patch('module.Foo') as mock:
...     instance = mock.return_value
...     instance.method.return_value = 'the result'
...     result = some_function()
...     assert result == 'the result'
1 dẫn đến một cuộc gọi đến phương thức
>>> def some_function():
...     instance = module.Foo()
...     return instance.method()
...
>>> with patch('module.Foo') as mock:
...     instance = mock.return_value
...     instance.method.return_value = 'the result'
...     result = some_function()
...     assert result == 'the result'
2:

>>> class ProductionClass:
...     def method(self):
...         self.something(1, 2, 3)
...     def something(self, a, b, c):
...         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)

Chế giễu phương thức gọi trên một đối tượng

Trong ví dụ cuối cùng, chúng tôi đã vá một phương thức trực tiếp trên một đối tượng để kiểm tra xem nó có được gọi chính xác không. Một trường hợp sử dụng phổ biến khác là chuyển một đối tượng vào một phương thức (hoặc một phần của hệ thống được kiểm tra) và sau đó kiểm tra xem nó có được sử dụng đúng cách không.

>>> def some_function():
...     instance = module.Foo()
...     return instance.method()
...
>>> with patch('module.Foo') as mock:
...     instance = mock.return_value
...     instance.method.return_value = 'the result'
...     result = some_function()
...     assert result == 'the result'
3 đơn giản dưới đây có phương pháp
>>> def some_function():
...     instance = module.Foo()
...     return instance.method()
...
>>> with patch('module.Foo') as mock:
...     instance = mock.return_value
...     instance.method.return_value = 'the result'
...     result = some_function()
...     assert result == 'the result'
4. Nếu nó được gọi với một đối tượng thì nó sẽ gọi
>>> def some_function():
...     instance = module.Foo()
...     return instance.method()
...
>>> with patch('module.Foo') as mock:
...     instance = mock.return_value
...     instance.method.return_value = 'the result'
...     result = some_function()
...     assert result == 'the result'
5 trên đó.

>>> class ProductionClass:
...     def closer(self, something):
...         something.close()
...

Vì vậy, để kiểm tra nó, chúng ta cần truyền trong một đối tượng bằng phương pháp

>>> def some_function():
...     instance = module.Foo()
...     return instance.method()
...
>>> with patch('module.Foo') as mock:
...     instance = mock.return_value
...     instance.method.return_value = 'the result'
...     result = some_function()
...     assert result == 'the result'
5 và kiểm tra xem nó có được gọi chính xác không.

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()

Chúng tôi không phải làm bất kỳ công việc nào để cung cấp phương thức ’đóng trên giả của chúng tôi. Truy cập gần tạo ra nó. Vì vậy, nếu ’Đóng, đã được gọi thì việc truy cập nó trong bài kiểm tra sẽ tạo ra nó, nhưng

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
9 sẽ làm tăng một ngoại lệ thất bại.

Các lớp học chế giễu

Một trường hợp sử dụng phổ biến là chế giễu các lớp được khởi tạo bởi mã của bạn đang được kiểm tra. Khi bạn vá một lớp, thì lớp đó được thay thế bằng một giả. Các thể hiện được tạo bằng cách gọi lớp. Điều này có nghĩa là bạn truy cập vào phiên bản Mock Mock bằng cách xem xét giá trị trả về của lớp bị chế giễu.

Trong ví dụ dưới đây, chúng tôi có hàm

>>> def some_function():
...     instance = module.Foo()
...     return instance.method()
...
>>> with patch('module.Foo') as mock:
...     instance = mock.return_value
...     instance.method.return_value = 'the result'
...     result = some_function()
...     assert result == 'the result'
8 khởi tạo
>>> def some_function():
...     instance = module.Foo()
...     return instance.method()
...
>>> with patch('module.Foo') as mock:
...     instance = mock.return_value
...     instance.method.return_value = 'the result'
...     result = some_function()
...     assert result == 'the result'
9 và gọi một phương thức trên đó. Cuộc gọi đến
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
00 thay thế lớp
>>> def some_function():
...     instance = module.Foo()
...     return instance.method()
...
>>> with patch('module.Foo') as mock:
...     instance = mock.return_value
...     instance.method.return_value = 'the result'
...     result = some_function()
...     assert result == 'the result'
9 bằng giả. Ví dụ
>>> def some_function():
...     instance = module.Foo()
...     return instance.method()
...
>>> with patch('module.Foo') as mock:
...     instance = mock.return_value
...     instance.method.return_value = 'the result'
...     result = some_function()
...     assert result == 'the result'
9 là kết quả của việc gọi giả, vì vậy nó được cấu hình bằng cách sửa đổi giả
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
03.

>>> def some_function():
...     instance = module.Foo()
...     return instance.method()
...
>>> with patch('module.Foo') as mock:
...     instance = mock.return_value
...     instance.method.return_value = 'the result'
...     result = some_function()
...     assert result == 'the result'

Đặt tên cho giả của bạn

Nó có thể hữu ích để đặt tên cho Mocks của bạn. Tên được hiển thị trong repr of the Mock và có thể hữu ích khi giả xuất hiện trong các thông báo thất bại thử nghiệm. Tên cũng được truyền đến các thuộc tính hoặc phương thức của giả:

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
0

Theo dõi tất cả các cuộc gọi

Thường thì bạn muốn theo dõi nhiều hơn một cuộc gọi đến một phương thức. Thuộc tính ____104 ghi lại tất cả các cuộc gọi đến các thuộc tính con của giả - và cả con cái của họ.

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
1

Nếu bạn đưa ra một khẳng định về

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
04 và bất kỳ phương pháp bất ngờ nào đã được gọi, thì khẳng định sẽ thất bại. Điều này rất hữu ích vì cũng như khẳng định rằng các cuộc gọi bạn dự kiến ​​đã được thực hiện, bạn cũng đang kiểm tra xem chúng đã được thực hiện đúng thứ tự và không có cuộc gọi bổ sung nào:

Bạn sử dụng đối tượng

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
06 để xây dựng danh sách để so sánh với
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
04:

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
2

Tuy nhiên, các tham số cho các cuộc gọi mà giả trả lại không được ghi lại, điều đó có nghĩa là không thể theo dõi các cuộc gọi lồng nhau trong đó các tham số được sử dụng để tạo tổ tiên là quan trọng:

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
3

Đặt giá trị trả về và các thuộc tính

Đặt các giá trị trả về trên một đối tượng giả là dễ dàng:

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
4

Tất nhiên bạn có thể làm tương tự cho các phương thức trên chế độ giả:

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
5

Giá trị trả về cũng có thể được đặt trong hàm tạo:

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
6

Nếu bạn cần cài đặt thuộc tính trên giả, chỉ cần làm điều đó:

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
7

Đôi khi bạn muốn chế giễu một tình huống phức tạp hơn, như ví dụ

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
08. Nếu chúng tôi muốn cuộc gọi này trả lại một danh sách, thì chúng tôi phải định cấu hình kết quả của cuộc gọi lồng nhau.

Chúng ta có thể sử dụng

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
06 để xây dựng tập hợp các cuộc gọi trong một cuộc gọi xích trực tiếp như thế này để dễ dàng khẳng định sau đó:

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
8

Đó là cuộc gọi đến

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
10 biến đối tượng cuộc gọi của chúng tôi thành một danh sách các cuộc gọi đại diện cho các cuộc gọi xích.

Tăng ngoại lệ với Mocks¶

Một thuộc tính hữu ích là

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
11. Nếu bạn đặt cái này thành một lớp hoặc thể hiện ngoại lệ thì ngoại lệ sẽ được nâng lên khi giả được gọi.

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
9

Chức năng tác dụng phụ và Iterables¶

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
11 cũng có thể được đặt thành một chức năng hoặc một điều không thể điều chỉnh được. Trường hợp sử dụng cho
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
11 như một điều có thể là nơi mà giả của bạn sẽ được gọi nhiều lần và bạn muốn mỗi cuộc gọi trả về một giá trị khác nhau. Khi bạn đặt
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
11 thành một cuộc gọi có thể lặp lại, việc giả sẽ trả về giá trị tiếp theo từ điều đáng tin cậy:

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

0

Đối với các trường hợp sử dụng nâng cao hơn, như thay đổi động, các giá trị trả về tùy thuộc vào những gì giả được gọi với,

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
11 có thể là một hàm. Hàm sẽ được gọi với các đối số tương tự như giả. Bất kể hàm nào trả về là những gì cuộc gọi trả về:

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

1

Chế giễu máy lặp không đồng bộ

Kể từ Python 3.8,

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
16 và
>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
5 có hỗ trợ để chế giễu các trình lặp không đồng bộ thông qua
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
18. Thuộc tính ____103 của
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
18 có thể được sử dụng để đặt các giá trị trả về được sử dụng để lặp.Asynchronous Iterators through
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
18. The
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
03 attribute of
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
18 can be used to set the return values to be used for iteration.

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

2

Chế tạo người quản lý bối cảnh không đồng bộ

Kể từ Python 3.8,

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
16 và
>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
5 có hỗ trợ để chế giễu các nhà quản lý bối cảnh không đồng bộ thông qua
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
23 và
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
24. Theo mặc định,
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
23 và
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
24 là các trường hợp
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
16 trả về hàm async.Asynchronous Context Managers through
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
23 and
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
24. By default,
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
23 and
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
24 are
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
16 instances that return an async function.

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

3

Tạo một sự giả từ một đối tượng hiện có

Một vấn đề với việc sử dụng quá mức việc chế giễu là nó kết hợp các bài kiểm tra của bạn để thực hiện giả của bạn hơn là mã thực của bạn. Giả sử bạn có một lớp thực hiện

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
28. Trong một bài kiểm tra cho một lớp khác, bạn cung cấp một chế độ giả của đối tượng này cũng cung cấp
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
28. Nếu sau này bạn tái cấu trúc lớp đầu tiên, do đó nó không còn có
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
28 - thì các bài kiểm tra của bạn sẽ tiếp tục vượt qua mặc dù mã của bạn đã bị hỏng!

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
2 cho phép bạn cung cấp một đối tượng như một đặc điểm kỹ thuật cho giả, sử dụng đối số từ khóa Spec. Truy cập các phương thức / thuộc tính trên giả don don tồn tại trên đối tượng đặc tả của bạn sẽ ngay lập tức gây ra lỗi thuộc tính. Nếu bạn thay đổi việc thực hiện đặc tả của mình, thì các bài kiểm tra sử dụng lớp đó sẽ bắt đầu thất bại ngay lập tức mà không cần bạn phải khởi tạo lớp trong các bài kiểm tra đó.

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

4

Sử dụng một đặc điểm kỹ thuật cũng cho phép khớp thông minh hơn các cuộc gọi được thực hiện với giả, bất kể một số tham số được truyền dưới dạng đối số vị trí hay được đặt tên:

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

5

Nếu bạn muốn kết hợp thông minh hơn này cũng hoạt động với các cuộc gọi phương thức trên giả, bạn có thể sử dụng tự động.auto-speccing.

Nếu bạn muốn một hình thức đặc tả mạnh mẽ hơn để ngăn chặn việc cài đặt các thuộc tính tùy ý cũng như nhận được chúng thì bạn có thể sử dụng spec_set thay vì spec.

Người trang trí bản vá

Ghi chú

Với

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
00, vấn đề là bạn vá các đối tượng trong không gian tên nơi chúng được nhìn lên. Điều này thường đơn giản, nhưng đối với một hướng dẫn nhanh, hãy đọc nơi để vá.where to patch.

Một nhu cầu phổ biến trong các thử nghiệm là vá một thuộc tính lớp hoặc thuộc tính mô -đun, ví dụ như vá lỗi hoặc vá một lớp trong một mô -đun để kiểm tra rằng nó được khởi tạo. Các mô -đun và các lớp có hiệu quả toàn cầu, vì vậy việc vá lỗi trên chúng phải được hoàn tác sau khi kiểm tra hoặc bản vá sẽ tồn tại trong các xét nghiệm khác và gây khó chẩn đoán các vấn đề.

Mock cung cấp ba nhà trang trí thuận tiện cho việc này:

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
00,
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
34 và
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
35.
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
36 lấy một chuỗi duy nhất, có dạng
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
37 để chỉ định thuộc tính bạn đang vá. Nó cũng tùy chọn lấy một giá trị mà bạn muốn thuộc tính (hoặc lớp hoặc bất cứ điều gì) được thay thế bằng. ‘Patch.Object, lấy một đối tượng và tên của thuộc tính bạn muốn được vá, cộng với giá trị tùy chọn để vá nó.

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
38:

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

6

Nếu bạn đang vá một mô -đun (bao gồm

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
39) thì hãy sử dụng
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
00 thay vì
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
34:

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

7

Tên mô -đun có thể được ‘chấm, ở dạng

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
42 nếu cần:

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

8

Một mô hình đẹp là thực sự trang trí các phương pháp kiểm tra:

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

9

Nếu bạn muốn vá bằng một giả, bạn có thể sử dụng

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
00 chỉ với một đối số (hoặc
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
34 với hai đối số). Mock sẽ được tạo cho bạn và được chuyển vào hàm / phương thức kiểm tra:

https://mock.readthedocs.io/en/latest/
mock>=2.0.0,<3.0
0

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

https://mock.readthedocs.io/en/latest/
mock>=2.0.0,<3.0
1

Khi bạn làm tổ trang trí, các chế độ giả được chuyển vào chức năng được trang trí theo cùng thứ tự chúng được áp dụng (thứ tự python bình thường mà các 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ụ phía trên chế độ giả cho

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
45 được thông qua đầu tiên.

Ngoài ra còn có

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
35 để thiết lập các giá trị trong một từ điển chỉ trong một phạm vi và khôi phục từ điển về trạng thái ban đầu của nó khi thử nghiệm kết thúc:

https://mock.readthedocs.io/en/latest/
mock>=2.0.0,<3.0
2

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
36,
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
38 và
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
49 đều có thể được sử dụng làm người quản lý bối cảnh.

Trường hợp bạn sử dụng

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
00 để tạo một chế giễu cho bạn, bạn có thể nhận được một tham chiếu đến giả sử dụng hình thức As As As của câu lệnh với câu lệnh:

https://mock.readthedocs.io/en/latest/
mock>=2.0.0,<3.0
3

Thay thế

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
36,
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
38 và
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
49 có thể được sử dụng làm người trang trí lớp. Khi được sử dụng theo cách này, nó giống như áp dụng riêng bộ trang trí cho mọi phương pháp có tên bắt đầu bằng cách kiểm tra.

Ví dụ thêm

Dưới đây là một số ví dụ khác cho một số kịch bản nâng cao hơn một chút.

Chế tạo các cuộc gọi xích

Các cuộc gọi xích thực sự thực sự đơn giản với giả một khi bạn hiểu thuộc tính

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
03. Khi một giả được gọi cho lần đầu tiên, hoặc bạn lấy ____103 của nó trước khi nó được gọi, một
>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
2 mới được tạo ra.

Điều này có nghĩa là bạn có thể thấy cách đối tượng được trả lại từ một cuộc gọi cho một đối tượng bị chế giễu đã được sử dụng bằng cách thẩm vấn giả ____103:

https://mock.readthedocs.io/en/latest/
mock>=2.0.0,<3.0
4

Từ đây, đó là một bước đơn giản để định cấu hình và sau đó đưa ra các xác nhận về các cuộc gọi bị xích. Tất nhiên, một giải pháp khác là viết mã của bạn theo cách có thể kiểm tra hơn ở nơi đầu tiên

Vì vậy, giả sử chúng ta có một số mã trông hơi như sau:

https://mock.readthedocs.io/en/latest/
mock>=2.0.0,<3.0
5

Giả sử rằng

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
58 đã được kiểm tra tốt, làm thế nào để chúng ta kiểm tra
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
59? Cụ thể, chúng tôi muốn kiểm tra rằng phần mã
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
60 sử dụng đối tượng phản hồi theo cách chính xác.

Vì chuỗi các cuộc gọi này được thực hiện từ một thuộc tính thể hiện, chúng tôi có thể vá cho thuộc tính

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
61 trên một thể hiện
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
62. Trong trường hợp cụ thể này, chúng tôi chỉ quan tâm đến giá trị trả lại từ cuộc gọi cuối cùng đến
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
63 vì vậy chúng tôi không có nhiều cấu hình để làm. Hãy để giả sử rằng đối tượng mà nó trả về là ‘giống như tệp, vì vậy chúng tôi sẽ đảm bảo rằng đối tượng phản hồi của chúng tôi sử dụng tích hợp
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
64 làm
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
65.

Để làm điều này, chúng tôi tạo một thể hiện giả khi phụ trợ giả của chúng tôi và tạo một đối tượng phản hồi giả cho nó. Để đặt phản hồi làm giá trị trả về cho

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
63 cuối cùng, chúng tôi có thể làm điều này:

https://mock.readthedocs.io/en/latest/
mock>=2.0.0,<3.0
6

Chúng ta có thể làm điều đó theo một cách đẹp hơn một chút bằng phương thức

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
67 để đặt trực tiếp giá trị trả về cho chúng tôi:

https://mock.readthedocs.io/en/latest/
mock>=2.0.0,<3.0
7

Với những điều này, chúng tôi đã vá bản vá lỗi của người Hồi giáo tại chỗ và có thể thực hiện cuộc gọi thực sự:

https://mock.readthedocs.io/en/latest/
mock>=2.0.0,<3.0
8

Sử dụng

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
04, chúng tôi có thể kiểm tra cuộc gọi xích với một khẳng định. Một cuộc gọi có chuỗi là một số cuộc gọi trong một dòng mã, vì vậy sẽ có một số mục trong
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
04. Chúng tôi có thể sử dụng
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
70 để tạo danh sách các cuộc gọi này cho chúng tôi:

https://mock.readthedocs.io/en/latest/
mock>=2.0.0,<3.0
9

Chế giễu một phần

Trong một số thử nghiệm, tôi muốn chế giễu cuộc gọi đến

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
71 để trả về một ngày đã biết, nhưng tôi đã không muốn ngăn mã được kiểm tra tạo các đối tượng ngày mới. Thật không may,
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
72 được viết bằng C, và vì vậy tôi không thể chỉ có một phương pháp
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
73 tĩnh.

Tôi đã tìm thấy một cách đơn giản để làm điều này liên quan đến việc kết thúc hiệu quả lớp ngày bằng một chế độ giả, nhưng chuyển qua các cuộc gọi đến nhà xây dựng cho lớp thực (và trả lại các trường hợp thực).

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
74 được sử dụng ở đây để chế giễu lớp
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
75 trong mô -đun được thử nghiệm. Thuộc tính
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
11 trên lớp ngày giả sau đó được đặt thành hàm lambda trả về một ngày thực. Khi lớp ngày giả được gọi là một ngày thực tế sẽ được xây dựng và trả lại bởi
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
11.

import unittest
import mock


class A:
    def __init__(self):
        self.size = 0

    def increment(self, amount):
        self.size += amount
        return amount

incr_return_values = [5, 10]


def square_func(*args):
    return incr_return_values.pop(0)


class TestMock(unittest.TestCase):

    @mock.patch.object(A, 'increment')
    def test_mock(self, A):
        A.increment.side_effect = square_func

        self.assertEqual(A.increment(1), 5)
        self.assertEqual(A.increment(-20), 10)
0

Lưu ý rằng chúng tôi không có bản vá

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
72 trên toàn cầu, chúng tôi vá
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
75 trong mô -đun sử dụng nó. Xem nơi để vá.where to patch.

Khi

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
73 được gọi là một ngày đã biết được trả về, nhưng các cuộc gọi đến hàm tạo
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
81 vẫn trả về ngày bình thường. Không có điều này, bạn có thể thấy mình phải tính toán kết quả dự kiến ​​bằng cách sử dụng chính xác cùng một thuật toán với mã được thử nghiệm, đây là một thử nghiệm cổ điển chống lại mẫu.

Các cuộc gọi đến Trình xây dựng ngày được ghi lại trong các thuộc tính

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
82 (
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
83 và bạn bè) cũng có thể hữu ích cho các bài kiểm tra của bạn.

Một cách khác để đối phó với ngày chế giễu, hoặc các lớp xây dựng khác, được thảo luận trong mục blog này.

Chế giễu phương thức máy phát điện

Trình tạo Python là một hàm hoặc phương thức sử dụng câu lệnh

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
84 để trả về một loạt các giá trị khi lặp lại trên 1.

Một phương thức / hàm máy phát được gọi để trả về đối tượng Trình tạo. Nó là đối tượng máy phát mà sau đó được lặp lại. Phương pháp giao thức để lặp là

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
85, vì vậy chúng ta có thể chế giễu điều này bằng cách sử dụng
>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
5.

Ở đây, một lớp ví dụ với một phương thức IT ITER được triển khai như một trình tạo:

import unittest
import mock


class A:
    def __init__(self):
        self.size = 0

    def increment(self, amount):
        self.size += amount
        return amount

incr_return_values = [5, 10]


def square_func(*args):
    return incr_return_values.pop(0)


class TestMock(unittest.TestCase):

    @mock.patch.object(A, 'increment')
    def test_mock(self, A):
        A.increment.side_effect = square_func

        self.assertEqual(A.increment(1), 5)
        self.assertEqual(A.increment(-20), 10)
1

Làm thế nào chúng ta sẽ chế giễu lớp này, và đặc biệt là phương pháp IT ITER của nó?

Để định cấu hình các giá trị được trả về từ lần lặp (ẩn trong cuộc gọi đến

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
87), chúng ta cần cấu hình đối tượng được trả về bởi cuộc gọi đến
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
88.

import unittest
import mock


class A:
    def __init__(self):
        self.size = 0

    def increment(self, amount):
        self.size += amount
        return amount

incr_return_values = [5, 10]


def square_func(*args):
    return incr_return_values.pop(0)


class TestMock(unittest.TestCase):

    @mock.patch.object(A, 'increment')
    def test_mock(self, A):
        A.increment.side_effect = square_func

        self.assertEqual(A.increment(1), 5)
        self.assertEqual(A.increment(-20), 10)
2

1

Ngoài ra còn có các biểu thức máy phát và sử dụng các máy phát điện nâng cao hơn, nhưng chúng tôi không quan tâm đến chúng ở đây. Một giới thiệu rất tốt về máy phát điện và chúng mạnh mẽ như thế nào: thủ thuật máy phát điện cho các lập trình viên hệ thống.

Áp dụng cùng một bản vá cho mọi phương pháp kiểm tra

Nếu bạn muốn một số bản vá tại chỗ cho nhiều phương pháp thử nghiệm, cách rõ ràng là áp dụng các bộ trang trí bản vá cho mọi phương pháp. Điều này có thể cảm thấy như sự lặp lại không cần thiết. Thay vào đó, bạn có thể sử dụng

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
00 (trong tất cả các hình thức khác nhau của nó) như một người trang trí lớp. Điều này áp dụng các bản vá cho tất cả các phương pháp kiểm tra trên lớp. Một phương thức kiểm tra được xác định bằng các phương thức có tên bắt đầu bằng
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
90:

import unittest
import mock


class A:
    def __init__(self):
        self.size = 0

    def increment(self, amount):
        self.size += amount
        return amount

incr_return_values = [5, 10]


def square_func(*args):
    return incr_return_values.pop(0)


class TestMock(unittest.TestCase):

    @mock.patch.object(A, 'increment')
    def test_mock(self, A):
        A.increment.side_effect = square_func

        self.assertEqual(A.increment(1), 5)
        self.assertEqual(A.increment(-20), 10)
3

Một cách khác để quản lý các bản vá là sử dụng các phương thức vá: Bắt đầu và dừng lại. Chúng cho phép bạn di chuyển các bản vá vào các phương thức

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
91 và
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
92 của bạn.patch methods: start and stop. These allow you to move the patching into your
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
91 and
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
92 methods.

import unittest
import mock


class A:
    def __init__(self):
        self.size = 0

    def increment(self, amount):
        self.size += amount
        return amount

incr_return_values = [5, 10]


def square_func(*args):
    return incr_return_values.pop(0)


class TestMock(unittest.TestCase):

    @mock.patch.object(A, 'increment')
    def test_mock(self, A):
        A.increment.side_effect = square_func

        self.assertEqual(A.increment(1), 5)
        self.assertEqual(A.increment(-20), 10)
4

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

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
93. Điều này có thể khó khăn hơn bạn nghĩ, bởi vì nếu một ngoại lệ được nêu ra trong thiết lập thì Teardown không được gọi.
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
94 làm cho điều này dễ dàng hơn:

import unittest
import mock


class A:
    def __init__(self):
        self.size = 0

    def increment(self, amount):
        self.size += amount
        return amount

incr_return_values = [5, 10]


def square_func(*args):
    return incr_return_values.pop(0)


class TestMock(unittest.TestCase):

    @mock.patch.object(A, 'increment')
    def test_mock(self, A):
        A.increment.side_effect = square_func

        self.assertEqual(A.increment(1), 5)
        self.assertEqual(A.increment(-20), 10)
5

Chế giễu các phương pháp không bị ràng buộc

Trong khi viết các bài kiểm tra ngày hôm nay, tôi cần phải vá một phương thức không liên kết (vá phương thức trên lớp thay vì trên ví dụ). Tôi cần phải được truyền vào như một đối số đầu tiên bởi vì tôi muốn đưa ra những khẳng định về những đối tượng nào đang gọi phương thức cụ thể này. Vấn đề là bạn không thể vá bằng một chế độ giả cho việc này, bởi vì nếu bạn thay thế một phương thức không liên kết bằng một chế độ giả, nó không trở thành một phương thức ràng buộc khi được tìm nạp từ trường hợp, và vì vậy nó không được tự truyền vào. Giải pháp thay thế là để vá phương pháp không liên kết với một hàm thực thay thế. Bộ trang trí

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
00 làm cho nó rất đơn giản để vá các phương thức với một chế độ giả phải tạo ra một chức năng thực sự trở thành một mối phiền toái.

Nếu bạn vượt qua

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
96 để vá thì nó thực hiện bản vá với một đối tượng chức năng thực. Đối tượng chức năng này có chữ ký tương tự như cái mà nó đang thay thế, nhưng ủy quyền cho một chế giễu dưới mui xe. Bạn vẫn nhận được tự động tạo tự động theo cách tương tự như trước đây. Mặc dù vậy, điều đó có nghĩa là nếu bạn sử dụng nó để vá một phương thức không liên kết trên một lớp, hàm bị chế giễu sẽ được biến thành một phương thức ràng buộc nếu nó được tìm nạp từ một thể hiện. Nó sẽ có
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
97 được thông qua như là đối số đầu tiên, đó chính xác là những gì tôi muốn:

import unittest
import mock


class A:
    def __init__(self):
        self.size = 0

    def increment(self, amount):
        self.size += amount
        return amount

incr_return_values = [5, 10]


def square_func(*args):
    return incr_return_values.pop(0)


class TestMock(unittest.TestCase):

    @mock.patch.object(A, 'increment')
    def test_mock(self, A):
        A.increment.side_effect = square_func

        self.assertEqual(A.increment(1), 5)
        self.assertEqual(A.increment(-20), 10)
6

Nếu chúng ta không sử dụng

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
96 thì phương pháp không liên kết được vá ra với một thể hiện giả thay thế, và được gọi là với
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
97.

Kiểm tra nhiều cuộc gọi với Mock¶

Mock có một API đẹp để đưa ra các xác nhận về cách sử dụng các đối tượng giả của bạn.

import unittest
import mock


class A:
    def __init__(self):
        self.size = 0

    def increment(self, amount):
        self.size += amount
        return amount

incr_return_values = [5, 10]


def square_func(*args):
    return incr_return_values.pop(0)


class TestMock(unittest.TestCase):

    @mock.patch.object(A, 'increment')
    def test_mock(self, A):
        A.increment.side_effect = square_func

        self.assertEqual(A.increment(1), 5)
        self.assertEqual(A.increment(-20), 10)
7

Nếu giả của bạn chỉ được gọi là một khi bạn có thể sử dụng phương thức

>>> def some_function():
...     instance = module.Foo()
...     return instance.method()
...
>>> with patch('module.Foo') as mock:
...     instance = mock.return_value
...     instance.method.return_value = 'the result'
...     result = some_function()
...     assert result == 'the result'
0 cũng khẳng định rằng
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
83 là một.

import unittest
import mock


class A:
    def __init__(self):
        self.size = 0

    def increment(self, amount):
        self.size += amount
        return amount

incr_return_values = [5, 10]


def square_func(*args):
    return incr_return_values.pop(0)


class TestMock(unittest.TestCase):

    @mock.patch.object(A, 'increment')
    def test_mock(self, A):
        A.increment.side_effect = square_func

        self.assertEqual(A.increment(1), 5)
        self.assertEqual(A.increment(-20), 10)
8

Cả

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

02 và
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

03 đều đưa ra các xác nhận về cuộc gọi gần đây nhất. Nếu giả của bạn sẽ được gọi là nhiều lần và bạn muốn đưa ra các xác nhận về tất cả những cuộc gọi đó bạn có thể sử dụng
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

04:

import unittest
import mock


class A:
    def __init__(self):
        self.size = 0

    def increment(self, amount):
        self.size += amount
        return amount

incr_return_values = [5, 10]


def square_func(*args):
    return incr_return_values.pop(0)


class TestMock(unittest.TestCase):

    @mock.patch.object(A, 'increment')
    def test_mock(self, A):
        A.increment.side_effect = square_func

        self.assertEqual(A.increment(1), 5)
        self.assertEqual(A.increment(-20), 10)
9

Người trợ giúp

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
06 giúp dễ dàng đưa ra các xác nhận về các cuộc gọi này. Bạn có thể xây dựng một danh sách các cuộc gọi mong đợi và so sánh nó với
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

04. Điều này trông rất giống với repr của
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

04:

>>> real = SomeClass()
>>> real.method = MagicMock(name='method')
>>> real.method(3, 4, 5, key='value')

0

Đối phó với các đối số có thể thay đổi

Một tình huống khác là rất hiếm, nhưng có thể cắn bạn, là khi giả của bạn được gọi với các đối số có thể thay đổi.

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

08 và
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

04 Lưu trữ tham chiếu đến các đối số. Nếu các đối số bị đột biến bởi mã được kiểm tra thì bạn không còn có thể đưa ra các xác nhận về những giá trị là gì khi giả được gọi.

Ở đây, một số mã ví dụ hiển thị vấn đề. Hãy tưởng tượng các chức năng sau được xác định trong ‘mymodule,:

>>> real = SomeClass()
>>> real.method = MagicMock(name='method')
>>> real.method(3, 4, 5, key='value')

1

Khi chúng tôi cố gắng kiểm tra rằng

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

10 gọi
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

11 với đối số chính xác, hãy xem những gì xảy ra:

>>> real = SomeClass()
>>> real.method = MagicMock(name='method')
>>> real.method(3, 4, 5, key='value')

2

Một khả năng sẽ là để Mock sao chép các đối số bạn truyền vào. Điều này sau đó có thể gây ra vấn đề nếu bạn thực hiện các khẳng định dựa vào nhận dạng đối tượng cho sự bình đẳng.

Ở đây, một giải pháp sử dụng chức năng

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
11. Nếu bạn cung cấp chức năng
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
11 cho một chế độ giả thì
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
11 sẽ được gọi với cùng một đối số như giả. Điều này cho chúng tôi một cơ hội để sao chép các đối số và lưu trữ chúng để xác nhận sau này. Trong ví dụ này, tôi đã sử dụng một chế độ giả khác để lưu trữ các đối số để tôi có thể sử dụng các phương thức giả để thực hiện khẳng định. Một lần nữa, một chức năng trợ giúp thiết lập điều này cho tôi.

>>> real = SomeClass()
>>> real.method = MagicMock(name='method')
>>> real.method(3, 4, 5, key='value')

3

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

15 được gọi với giả sẽ được gọi. Nó trả lại một giả mới mà chúng tôi thực hiện khẳng định. Hàm
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
11 tạo một bản sao của ARG và gọi
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

17 của chúng tôi với bản sao.

Ghi chú

Nếu giả của bạn sẽ chỉ được sử dụng một khi có một cách dễ dàng hơn để kiểm tra các đối số tại điểm chúng được gọi. Bạn chỉ có thể thực hiện kiểm tra bên trong hàm

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
11.

>>> real = SomeClass()
>>> real.method = MagicMock(name='method')
>>> real.method(3, 4, 5, key='value')

4

Một cách tiếp cận khác là tạo ra một lớp con của

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
2 hoặc
>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
5 sao chép (sử dụng
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

21) các đối số. Ở đây, một ví dụ thực hiện:

>>> real = SomeClass()
>>> real.method = MagicMock(name='method')
>>> real.method(3, 4, 5, key='value')

5

Khi bạn phân lớp

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
2 hoặc
>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
5 tất cả các thuộc tính được tạo động và
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
03 sẽ tự động sử dụng lớp con của bạn. Điều đó có nghĩa là tất cả trẻ em của
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

25 cũng sẽ có loại
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

25.

Các mảng làm tổ

Sử dụng bản vá làm trình quản lý ngữ cảnh là tốt, nhưng nếu bạn thực hiện nhiều bản vá, bạn có thể kết thúc với nhau với các câu lệnh thụt hơn và xa hơn về bên phải:

>>> real = SomeClass()
>>> real.method = MagicMock(name='method')
>>> real.method(3, 4, 5, key='value')

6

Với các chức năng

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

27 ____227 và các phương thức vá lỗi: Bắt đầu và dừng chúng ta có thể đạt được hiệu ứng tương tự mà không cần thụt lề lồng nhau. Một phương pháp trợ giúp đơn giản,
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

28, đặt bản vá vào vị trí và trả về chế độ giả tạo cho chúng tôi:patch methods: start and stop we can achieve the same effect without the nested indentation. A simple helper method,
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

28, puts the patch in place and returns the created mock for us:

>>> real = SomeClass()
>>> real.method = MagicMock(name='method')
>>> real.method(3, 4, 5, key='value')

7

Chế giễu một từ điển với ma thuật

Bạn có thể muốn chế giễu một từ điển hoặc đối tượng container khác, ghi lại tất cả quyền truy cập vào nó trong khi nó vẫn hoạt động như một từ điển.

Chúng ta có thể làm điều này với

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
5, sẽ hoạt động như một từ điển và sử dụng
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
11 để ủy thác truy cập từ điển đến một từ điển thực sự nằm dưới sự kiểm soát của chúng ta.

Khi các phương thức

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

31 và
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

32 của
>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
5 của chúng tôi được gọi (truy cập từ điển bình thường) thì
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
11 được gọi bằng khóa (và trong trường hợp
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

35 cũng có giá trị). Chúng tôi cũng có thể kiểm soát những gì được trả lại.

Sau khi

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
5 đã được sử dụng, chúng tôi có thể sử dụng các thuộc tính như
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

04 để khẳng định cách sử dụng từ điển:

>>> real = SomeClass()
>>> real.method = MagicMock(name='method')
>>> real.method(3, 4, 5, key='value')

8

Ghi chú

Nếu giả của bạn sẽ chỉ được sử dụng một khi có một cách dễ dàng hơn để kiểm tra các đối số tại điểm chúng được gọi. Bạn chỉ có thể thực hiện kiểm tra bên trong hàm

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
11.

>>> real = SomeClass()
>>> real.method = MagicMock(name='method')
>>> real.method(3, 4, 5, key='value')

9

Một cách tiếp cận khác là tạo ra một lớp con của

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
2 hoặc
>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
5 sao chép (sử dụng
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

21) các đối số. Ở đây, một ví dụ thực hiện:

>>> class ProductionClass:
...     def method(self):
...         self.something(1, 2, 3)
...     def something(self, a, b, c):
...         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
0

Khi bạn phân lớp

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
2 hoặc
>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
5 tất cả các thuộc tính được tạo động và
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
03 sẽ tự động sử dụng lớp con của bạn. Điều đó có nghĩa là tất cả trẻ em của
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

25 cũng sẽ có loại
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

25.

>>> class ProductionClass:
...     def method(self):
...         self.something(1, 2, 3)
...     def something(self, a, b, c):
...         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
1

Các mảng làm tổ

>>> class ProductionClass:
...     def method(self):
...         self.something(1, 2, 3)
...     def something(self, a, b, c):
...         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
2

Sử dụng bản vá làm trình quản lý ngữ cảnh là tốt, nhưng nếu bạn thực hiện nhiều bản vá, bạn có thể kết thúc với nhau với các câu lệnh thụt hơn và xa hơn về bên phải:

Với các chức năng

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

27 ____227 và các phương thức vá lỗi: Bắt đầu và dừng chúng ta có thể đạt được hiệu ứng tương tự mà không cần thụt lề lồng nhau. Một phương pháp trợ giúp đơn giản,
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

28, đặt bản vá vào vị trí và trả về chế độ giả tạo cho chúng tôi:

>>> class ProductionClass:
...     def method(self):
...         self.something(1, 2, 3)
...     def something(self, a, b, c):
...         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
3

Chế giễu một từ điển với ma thuật

>>> class ProductionClass:
...     def method(self):
...         self.something(1, 2, 3)
...     def something(self, a, b, c):
...         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
4

Bạn có thể muốn chế giễu một từ điển hoặc đối tượng container khác, ghi lại tất cả quyền truy cập vào nó trong khi nó vẫn hoạt động như một từ điển.

Chúng ta có thể làm điều này với

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
5, sẽ hoạt động như một từ điển và sử dụng
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
11 để ủy thác truy cập từ điển đến một từ điển thực sự nằm dưới sự kiểm soát của chúng ta.

>>> class ProductionClass:
...     def method(self):
...         self.something(1, 2, 3)
...     def something(self, a, b, c):
...         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
5

2

Khi các phương thức

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

31 và
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

32 của
>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
5 của chúng tôi được gọi (truy cập từ điển bình thường) thì
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
11 được gọi bằng khóa (và trong trường hợp
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

35 cũng có giá trị). Chúng tôi cũng có thể kiểm soát những gì được trả lại.

Sau khi >>> real = ProductionClass() >>> mock = Mock() >>> real.closer(mock) >>> mock.close.assert_called_with() 5 đã được sử dụng, chúng tôi có thể sử dụng các thuộc tính như import mock ... ... @mock.patch.object(ClassB, 'method_2') @mock.patch.object(ClassA, 'method_1') def test_same_method_multi_return_value(self, method_1, method_2): # type: () -> None method_1.return_value = 'Static value' method_1.side_effect = [ 'Value called by first time' 'Value called by second time' '...' ] 04 để khẳng định cách sử dụng từ điển:

Một giải pháp thay thế cho việc sử dụng

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
5 là sử dụng
>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
2 và chỉ cung cấp các phương thức ma thuật mà bạn đặc biệt muốn:

Nói chung nhập khẩu địa phương là phải tránh. Đôi khi chúng được thực hiện để ngăn chặn các phụ thuộc tròn, thường có cách tốt hơn nhiều để giải quyết vấn đề (tái cấu trúc mã) hoặc để ngăn chặn chi phí phía trước của Hồi giáo bằng cách trì hoãn việc nhập khẩu. Điều này cũng có thể được giải quyết theo những cách tốt hơn so với nhập cục bộ vô điều kiện (lưu trữ mô -đun dưới dạng thuộc tính lớp hoặc mô -đun và chỉ thực hiện nhập khi sử dụng đầu tiên).

Điều đó sang một bên có một cách để sử dụng

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

43 để ảnh hưởng đến kết quả nhập khẩu. Nhập dữ liệu tìm thấy một đối tượng từ từ điển
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

55. Lưu ý rằng nó lấy một đối tượng, không cần phải là một mô -đun. Nhập một mô -đun cho lần đầu tiên dẫn đến một đối tượng mô -đun được đặt vào
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

55, vì vậy thông thường khi bạn nhập một cái gì đó, bạn sẽ lấy lại một mô -đun. Điều này không cần phải là trường hợp tuy nhiên.

Điều này có nghĩa là bạn có thể sử dụng

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
35 để tạm thời đặt một chế độ giả vào vị trí trong
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

55. Bất kỳ nhập khẩu nào trong khi bản vá này hoạt động sẽ lấy được giả. Khi bản vá hoàn tất (chức năng được trang trí thoát ra, phần thân câu lệnh đã hoàn thành hoặc
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

59 được gọi) thì bất cứ điều gì trước đây sẽ được khôi phục an toàn.

Dưới đây, một ví dụ đã chế giễu mô -đun ‘Fooble.

>>> class ProductionClass:
...     def method(self):
...         self.something(1, 2, 3)
...     def something(self, a, b, c):
...         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
6

Như bạn có thể thấy

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

60 thành công, nhưng khi thoát ra, không còn ‘fooble, còn lại trong
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

55.

Điều này cũng hoạt động cho mẫu

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

62:

>>> class ProductionClass:
...     def method(self):
...         self.something(1, 2, 3)
...     def something(self, a, b, c):
...         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
7

Với nhiều công việc hơn một chút, bạn cũng có thể chế giễu nhập khẩu gói:

>>> class ProductionClass:
...     def method(self):
...         self.something(1, 2, 3)
...     def something(self, a, b, c):
...         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
8

Theo dõi thứ tự các cuộc gọi và ít xác nhận cuộc gọi dài dòng

Lớp

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
2 cho phép bạn theo dõi thứ tự các cuộc gọi phương thức trên các đối tượng giả của bạn thông qua thuộc tính
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

64. Điều này không cho phép bạn theo dõi thứ tự các cuộc gọi giữa các đối tượng giả riêng biệt, tuy nhiên chúng ta có thể sử dụng
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
04 để đạt được hiệu ứng tương tự.

Bởi vì chế giễu các cuộc gọi theo dõi các chế độ giả của trẻ em trong

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
04 và truy cập vào một thuộc tính tùy ý của một chế giễu tạo ra một chế độ giả, chúng ta có thể tạo các chế giễu riêng biệt từ cha mẹ. Các cuộc gọi đến những con chó con đó sau đó sẽ được ghi lại, theo thứ tự, trong
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
04 của cha mẹ:

>>> class ProductionClass:
...     def method(self):
...         self.something(1, 2, 3)
...     def something(self, a, b, c):
...         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
9

>>> class ProductionClass:
...     def closer(self, something):
...         something.close()
...
0

>>> class ProductionClass:
...     def closer(self, something):
...         something.close()
...
1

Sau đó, chúng ta có thể khẳng định về các cuộc gọi, bao gồm cả thứ tự, bằng cách so sánh với thuộc tính

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
04 trên trình quản lý giả:

>>> class ProductionClass:
...     def closer(self, something):
...         something.close()
...
2

Nếu

@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
36 đang tạo và đặt vào vị trí, chế giễu của bạn thì bạn có thể gắn chúng vào chế tạo người quản lý bằng phương pháp
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

70. Sau khi đính kèm các cuộc gọi sẽ được ghi lại trong
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
04 của người quản lý.

>>> class ProductionClass:
...     def closer(self, something):
...         something.close()
...
3

Nếu nhiều cuộc gọi đã được thực hiện, nhưng bạn chỉ quan tâm đến một chuỗi cụ thể của chúng thì một giải pháp thay thế là sử dụng phương thức

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

72. Điều này lấy một danh sách các cuộc gọi (được xây dựng với đối tượng
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
06). Nếu chuỗi cuộc gọi đó nằm trong
@mock.patch("A.increment")
def test_method(self, mock_increment):
    mock_increment.side_effect = [5,10]
    self.assertEqual(mock_increment(), 5)
    self.assertEqual(mock_increment(), 10)
04 thì khẳng định thành công.

>>> class ProductionClass:
...     def closer(self, something):
...         something.close()
...
4

Mặc dù cuộc gọi xích

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

75 không phải là cuộc gọi duy nhất được thực hiện cho Mock, nhưng Assert vẫn thành công.

Đôi khi một chế giễu có thể có một số cuộc gọi được thực hiện cho nó, và bạn chỉ quan tâm đến việc khẳng định về một số cuộc gọi đó. Bạn thậm chí có thể không quan tâm đến đơn đặt hàng. Trong trường hợp này, bạn có thể chuyển

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

76 đến
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

77:

>>> class ProductionClass:
...     def closer(self, something):
...         something.close()
...
5

Đối số phức tạp hơn phù hợp

Sử dụng cùng một khái niệm cơ bản như

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

78, chúng ta có thể triển khai các trình chỉnh sửa để thực hiện các xác nhận phức tạp hơn trên các đối tượng được sử dụng làm đối số để chế giễu.

Giả sử chúng ta hy vọng một số đối tượng sẽ được chuyển đến một chế độ giả mà theo mặc định so sánh bằng nhau dựa trên nhận dạng đối tượng (đó là mặc định Python cho các lớp do người dùng xác định). Để sử dụng

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
9, chúng ta sẽ cần phải vượt qua cùng một đối tượng. Nếu chúng ta chỉ quan tâm đến một số thuộc tính của đối tượng này thì chúng ta có thể tạo một trình kết hợp sẽ kiểm tra các thuộc tính này cho chúng ta.

Bạn có thể thấy trong ví dụ này cách một cuộc gọi ’tiêu chuẩn đến

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

02 là đủ:

>>> class ProductionClass:
...     def closer(self, something):
...         something.close()
...
6

Một chức năng so sánh cho lớp

>>> def some_function():
...     instance = module.Foo()
...     return instance.method()
...
>>> with patch('module.Foo') as mock:
...     instance = mock.return_value
...     instance.method.return_value = 'the result'
...     result = some_function()
...     assert result == 'the result'
9 của chúng tôi có thể trông giống như thế này:

>>> class ProductionClass:
...     def closer(self, something):
...         something.close()
...
7

Và một đối tượng Matcher có thể sử dụng các hàm so sánh như thế này cho hoạt động bình đẳng của nó sẽ trông giống như thế này:

>>> class ProductionClass:
...     def closer(self, something):
...         something.close()
...
8

Đặt tất cả những điều này lại với nhau:

>>> class ProductionClass:
...     def closer(self, something):
...         something.close()
...
9

import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

82 được khởi tạo với hàm so sánh của chúng tôi và đối tượng
>>> def some_function():
...     instance = module.Foo()
...     return instance.method()
...
>>> with patch('module.Foo') as mock:
...     instance = mock.return_value
...     instance.method.return_value = 'the result'
...     result = some_function()
...     assert result == 'the result'
9 mà chúng tôi muốn so sánh với. Trong
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

02, phương pháp bình đẳng
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

82 sẽ được gọi, so sánh đối tượng mà giả được gọi với phương pháp mà chúng tôi đã tạo ra Matcher của chúng tôi. Nếu chúng phù hợp thì
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

02 vượt qua và nếu họ không có một
import mock

...
...

@mock.patch.object(ClassB, 'method_2')
@mock.patch.object(ClassA, 'method_1')
def test_same_method_multi_return_value(self, method_1, method_2):
    # type: () -> None

    method_1.return_value = 'Static value'
    method_1.side_effect = [
        'Value called by first time'
        'Value called by second time'
        '...'
    ]

87 sẽ được nâng lên:

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
0

Với một chút điều chỉnh, bạn có thể có chức năng so sánh trực tiếp tăng ____287 và cung cấp một thông báo thất bại hữu ích hơn.

Kể từ phiên bản 1.5, thư viện thử nghiệm Python Pyhamcrest cung cấp chức năng tương tự, có thể hữu ích ở đây, dưới dạng trình mai tởm bình đẳng của nó (Hamcrest.l Library.Integration.Match_Equality).

Side_effect trong giả trăn là gì?

Side_Effect: Một chức năng được gọi bất cứ khi nào giả được gọi. Xem thuộc tính Side_Effect. Hữu ích để nâng cao các ngoại lệ hoặc thay đổi một cách tự động các giá trị trả về. Hàm được gọi với cùng một đối số với giả và trừ khi nó trả về mặc định, giá trị trả về của hàm này được sử dụng làm giá trị trả về.A function to be called whenever the Mock is called. See the side_effect attribute. Useful for raising exceptions or dynamically changing return values. The function is called with the same arguments as the mock, and unless it returns DEFAULT , the return value of this function is used as the return value.

Sự khác biệt giữa Mock và Magicmock là gì?

Vậy sự khác biệt giữa họ là gì?MagicMock là một lớp con của Mock.Nó chứa tất cả các phương pháp ma thuật được tạo sẵn và sẵn sàng sử dụng (ví dụ: __str__, __len__, v.v.).Do đó, bạn nên sử dụng MagicMock khi bạn cần Phương pháp ma thuật và chế giễu nếu bạn không cần chúng.MagicMock is a subclass of Mock . It contains all magic methods pre-created and ready to use (e.g. __str__ , __len__ , etc.). Therefore, you should use MagicMock when you need magic methods, and Mock if you don't need them.

@Patch trong Python là gì?

Điều này, cùng với các lớp con của nó, sẽ đáp ứng hầu hết các nhu cầu chế giễu Python mà bạn sẽ phải đối mặt trong các bài kiểm tra của mình.Thư viện cũng cung cấp một hàm, được gọi là Patch (), thay thế các đối tượng thực trong mã của bạn bằng các phiên bản giả.replaces the real objects in your code with Mock instances.

Magicmock là gì?

MagicMock.Các đối tượng MagicMock cung cấp một giao diện chế giễu đơn giản cho phép bạn đặt giá trị trả về hoặc hành vi khác của chức năng hoặc cuộc gọi tạo đối tượng mà bạn đã vá.Điều này cho phép bạn xác định đầy đủ hành vi của cuộc gọi và tránh tạo các đối tượng thực, có thể gây khó chịu.provide a simple mocking interface that allows you to set the return value or other behavior of the function or object creation call that you patched. This allows you to fully define the behavior of the call and avoid creating real objects, which can be onerous.