Python giả nhiều giá trị trả về


168

Tôi đang sử dụng pythons mock.patch và muốn thay đổi giá trị trả lại cho mỗi cuộc gọi. Đây là lời cảnh báo: chức năng được vá không có đầu vào, vì vậy tôi không thể thay đổi giá trị trả về dựa trên đầu vào.

Đây là mã của tôi để tham khảo.

def get_boolean_response():
    response = io.prompt('y/n').lower()
    while response not in ('y', 'n', 'yes', 'no'):
        io.echo('Not a valid input. Try again'])
        response = io.prompt('y/n').lower()

    return response in ('y', 'yes')

Mã kiểm tra của tôi:

@mock.patch('io')
def test_get_boolean_response(self, mock_io):
    #setup
    mock_io.prompt.return_value = ['x','y']
    result = operations.get_boolean_response()

    #test
    self.assertTrue(result)
    self.assertEqual(mock_io.prompt.call_count, 2)

io.promptchỉ là một phiên bản "đầu vào" độc lập trên nền tảng (python 2 và 3). Vì vậy, cuối cùng tôi đang cố gắng để chế nhạo đầu vào của người dùng. Tôi đã thử sử dụng một danh sách cho giá trị trả về, nhưng nó không hoạt động.

Bạn có thể thấy rằng nếu giá trị trả về là một cái gì đó không hợp lệ, tôi sẽ chỉ nhận được một vòng lặp vô hạn ở đây. Vì vậy, tôi cần một cách để cuối cùng thay đổi giá trị trả về, để thử nghiệm của tôi thực sự kết thúc.

(một cách khác có thể để trả lời câu hỏi này có thể là giải thích cách tôi có thể bắt chước đầu vào của người dùng trong bài kiểm tra đơn vị)


Không phải là một bản sao của câu hỏi này chủ yếu vì tôi không có khả năng thay đổi đầu vào.

Một trong những ý kiến ​​của Câu trả lời cho câu hỏi này là cùng một dòng, nhưng không có câu trả lời / nhận xét nào được cung cấp.


3
response is not 'y' or 'n' or 'yes' or 'no'trong không làm những gì bạn nghĩ rằng nó. Xem Làm cách nào để kiểm tra một biến đối với nhiều giá trị? và bạn không nên sử dụng isđể so sánh các giá trị chuỗi, sử dụng ==để so sánh các giá trị , không phải danh tính đối tượng.
Martijn Pieters

Cũng cẩn thận ở đây. Có vẻ như bạn đang cố gắng sử dụng isđể so sánh chuỗi ký tự. Đừng làm vậy. Thực tế là nó hoạt động (đôi khi) chỉ là một chi tiết triển khai trong CPython. Ngoài ra, response is not 'y' or 'n' or 'yes' or 'no'có lẽ không làm những gì bạn nghĩ đó là ...
mgilson

Câu trả lời:


300

Bạn có thể gán một lần lặp cho side_effectvà giả sẽ trả về giá trị tiếp theo trong chuỗi mỗi lần nó được gọi:

>>> from unittest.mock import Mock
>>> m = Mock()
>>> m.side_effect = ['foo', 'bar', 'baz']
>>> m()
'foo'
>>> m()
'bar'
>>> m()
'baz'

Trích dẫn Mock()tài liệu :

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

Như một bên, thử nghiệm response is not 'y' or 'n' or 'yes' or 'no'sẽ không hoạt động; bạn đang hỏi xem biểu thức (response is not 'y')là đúng hay 'y'đúng (luôn luôn như vậy, một chuỗi không trống luôn luôn đúng), v.v ... Các biểu thức khác nhau ở hai bên của ortoán tử là độc lập . Xem Làm cách nào để kiểm tra một biến đối với nhiều giá trị?

Bạn cũng không nên sử dụng isđể kiểm tra đối với một chuỗi. Trình thông dịch CPython có thể sử dụng lại các đối tượng chuỗi trong một số trường hợp nhất định , nhưng đây không phải là hành vi bạn nên dựa vào.

Như vậy, sử dụng:

response not in ('y', 'n', 'yes', 'no')

thay thế; điều này sẽ sử dụng các phép kiểm bằng ( ==) để xác định xem responsetham chiếu một chuỗi có cùng nội dung (giá trị) hay không.

Áp dụng tương tự cho response == 'y' or 'yes'; sử dụng response in ('y', 'yes')thay thế.


Có cách nào để làm điều này với tiêu chuẩn mock? Có cách nào để sử dụng bản vá với MagicMock như tôi đang làm với bản giả tiêu chuẩn không?
Nick Humrich

@Humdinger: Đây là một tính năng của Mocklớp stardard .
Martijn Pieters

17
Chỉ định một danh sách dường như chỉ hoạt động với python 3. Thử nghiệm với python 2.7 Tôi cần sử dụng một iterator thay thế ( m.side_effect = iter(['foo', 'bar', 'baz'])).
dùng686249

1
@ user686249: Tôi thực sự có thể tái tạo điều này, bởi vì việc xác định từ một phương thức tạo ra một lambda(một hàm), chứ không phải a MagicMock. Một đối tượng chức năng không thể tài sản, vì vậy các side_effectthuộc tính được một iterable. Bạn không nên suy đoán phương pháp như vậy mặc dù. Sử dụng tốt hơn mock.patch.object(requests.Session, 'post'); dẫn đến một đối tượng patcher tự động xác định chính xác phương thức hỗ trợ side_effectđúng cách.
Martijn Pieters

3
@ JoeMjr2: Khi iterator cạn kiệt, StopIterationđược nâng lên. Bạn có thể sử dụng bất kỳ trình vòng lặp nào, vì vậy bạn có thể sử dụng itertools.chain(['Foo'], itertools.repeat('Bar'))để sản xuất Foomột lần, sau đó mãi mãi sản xuất Bar.
Martijn Pieters
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.