Pythonは複数の戻り値を模擬


168

私はpythons mock.patchを使用していて、呼び出しごとに戻り値を変更したいと思います。ここで注意点があります。パッチを適用する関数には入力がないため、入力に基づいて戻り値を変更できません。

これが参考のための私のコードです。

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')

私のテストコード:

@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.prompt「入力」のプラットフォームに依存しない(python 2および3)バージョンです。したがって、最終的にはユーザーの入力を模倣しようとしています。戻り値にリストを使用してみましたが、それではうまくいきません。

戻り値が無効な場合、ここで無限ループが発生することがわかります。したがって、最終的に戻り値を変更して、テストが実際に終了する方法が必要です。

(この質問に答える別の可能な方法は、ユニットテストでユーザー入力をどのように模倣できるかを説明することです)


私が入力を変える能力がないので、この質問の重複ではありません。

この質問に対する回答のコメントの1つは同じ行に沿っていますが、回答/コメントは提供されていません。


3
response is not 'y' or 'n' or 'yes' or 'no'はないあなたはそれがない何を考えてやって。1つの変数を複数の値に対してテストするにどうすればよいですか?を参照してくださいまた、文字列値の比較に使用isないでください。オブジェクトIDではなく、値の==比較に使用してください。
Martijn Pieters

ここでも注意してください。is文字列リテラルの比較に使用しようとしているようです。しないでください。(時々)動作するという事実は、CPythonの実装の詳細にすぎません。また、response is not 'y' or 'n' or 'yes' or 'no'おそらくあなたが思っていることを実行していない...
mgilson 14

回答:


300

イテラブルside_effectに割り当てると、モックは呼び出されるたびにシーケンスの次の値を返します。

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

Mock()ドキュメントの引用:

side_effectが反復可能である場合、モックを呼び出すたびに、反復可能から次の値が返されます。

余談ですが、テストresponse is not 'y' or 'n' or 'yes' or 'no'は機能しません。式(response is not 'y')がtrueであるか、trueであるか'y'(常に大文字であり、空でない文字列は常にtrueであるか)などを尋ねています。or演算子の両側のさまざまな式は独立しています。1つの変数を複数の値に対してテストするにどうすればよいですか?を参照してください

またis、文字列に対するテストに使用しないでください。CPythonインタープリター特定の状況下で文字列オブジェクトを再利用する場合がありますが、これは信頼できる動作ではありません。

そのため、以下を使用します。

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

代わりに; これは、等価テスト(==)を使用responseして、同じ内容(値)の文字列を参照しているかどうかを判断します。

同じことが当てはまりresponse == 'y' or 'yes'ます。response in ('y', 'yes')代わりに使用してください。


これを標準で行う方法はありmockますか?標準のモックで行うように、MagicMockでパッチを使用する方法はありますか?
Nick Humrich 14

@Humdinger:これはstardard Mockクラスの機能です。
Martijn Pieters

17
リストの割り当ては、Python 3でのみ機能するようです。Python 2.7でのテスト代わりにイテレータを使用する必要があります(m.side_effect = iter(['foo', 'bar', 'baz']))。
user686249

1
@ user686249:メソッドからのスペシフィケーションではlambda(関数)ではなくが生成されるため、これを実際に再現できMagicMockます。関数オブジェクトプロパティを持つことができないため、side_effect属性反復可能でなければなりません。ただし、そのような方法を指定する必要はありません。よりよく使うmock.patch.object(requests.Session, 'post'); パッチャーのオブジェクトで結果を適切法上の自動スペック、そのことサポートside_effect適切に。
Martijn Pieters

3
@ JoeMjr2:イテレータが使い果たされると、StopIteration発生します。どのイテレータでも使用できるので、一度itertools.chain(['Foo'], itertools.repeat('Bar'))作成しFooてから永久に作成することができますBar
Martijn Pieters
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.