web-dev-qa-db-ja.com

Python複数の戻り値のモック

私は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つは同じ行に沿っていますが、回答/コメントは提供されていません。

113
Nick Humrich

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

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

Mock() documentation の引用:

side_effectが反復可能な場合、モックの各呼び出しは反復可能な要素から次の値を返します。

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

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

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

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

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

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

211
Martijn Pieters