私はPythonで書かれたコンソールプログラムを持っています。これは、コマンドを使用して、ユーザーの質問をする:Pytest:入力呼び出しで関数をテストする方法は?
some_input = input('Answer the question:', ...)
どのように私はpytest
を使用してinput
への呼び出しを含む関数をテストするのでしょうか? テスターが1回のテストを終了するために何度も何度もテキストを入力することを強制したくありません。
私はPythonで書かれたコンソールプログラムを持っています。これは、コマンドを使用して、ユーザーの質問をする:Pytest:入力呼び出しで関数をテストする方法は?
some_input = input('Answer the question:', ...)
どのように私はpytest
を使用してinput
への呼び出しを含む関数をテストするのでしょうか? テスターが1回のテストを終了するために何度も何度もテキストを入力することを強制したくありません。
おそらく内蔵のinput
機能をモックする必要があり、あなたは、各テストの後に元のinput
機能に戻すpytest
によって提供さteardown
機能を使用することができます。
import module # The module which contains the call to input
class TestClass:
def test_function_1(self):
# Override the Python built-in input method
module.input = lambda: 'some_input'
# Call the function you would like to test (which uses input)
output = module.function()
assert output == 'expected_output'
def test_function_2(self):
module.input = lambda: 'some_other_input'
output = module.function()
assert output == 'another_expected_output'
def teardown_method(self, method):
# This method is being called after each test case, and it will revert input back to original function
module.input = input
よりエレガントな解決策は、with statement
と共にmock
モジュールを使用することであろう。この方法ではティアダウンを使用する必要はなく、パッチ適用された方法はwith
スコープ内にしか存在しません。
import mock
import module
def test_function():
with mock.patch.object(__builtin__, 'input', lambda: 'some_input'):
assert module.function() == 'expected_output'
コンパイラが示唆したように、pytestにはこのための新しいMonkeypatch Fixtureがあります。 monkeypatchオブジェクトは、クラス内の属性または辞書内の値を変更してから、テストの最後に元の値に戻すことができます。この場合
、ビルトインinput
関数はPythonの__builtins__
辞書の値であるので、我々はそうのようにそれを変更することができます。
def test_something_that_involves_user_input(monkeypatch):
# monkeypatch the "input" function, so that it returns "Mark".
# This simulates the user entering "Mark" in the terminal:
monkeypatch.setitem('builtins.input', lambda x: "Mark")
# go about using input() like you normally would:
i = input("What is your name?")
assert i == "Mark"
編集:あなたは置き換えることができlambda x: "Mark"
これは 'setitem'ではなく' setattr'でなければなりません。 – Matt
からlambda: "Mark"
を変更しますファイルまたはインメモリのStringIOバッファからの入力のような何らかのカスタムText IOのsys.stdin
を使用します。
import sys
class Test:
def test_function(self):
sys.stdin = open("preprogrammed_inputs.txt")
module.call_function()
def setup_method(self):
self.orig_stdin = sys.stdin
def teardown_method(self):
sys.stdin = self.orig_stdin
これは、input()
にパッチするだけの場合よりも堅牢です。これは、モジュールがstdinからテキストを消費する他の方法を使用している場合には十分ではありません。
これは、カスタムコンテキストマネージャ
import sys
from contextlib import contextmanager
@contextmanager
def replace_stdin(target):
orig = sys.stdin
sys.stdin = target
yield
sys.stdin = orig
でかなりエレガントにもを行うことそしてちょうど例えば、このようにそれを使用することができます:
with replace_stdin(StringIO("some preprogrammed input")):
module.call_function()
次のようにあなたがmock.patch
でそれを行うことができます。
まず、あなたのコードでは、通話のためのダミー関数を作成input
へ:あなたのテスト機能で
def __get_input(text):
return input(text)
:あなたがしている場合
import my_module
from mock import patch
@patch('my_module.__get_input', return_value='y'))
def test_what_happens_when_answering_yes(self, mock):
"""
Test what happens when user input is 'y'
"""
# whatever your test function does
は、例えば、ループがいることを確認します有効な答えは['y'、 'Y'、 'n'、 'N']のみで、別の値を入力しても何も起こらないことをテストできます。この場合
私たちは「N」に答えるとき
SystemExit
が発生したと仮定します。
@patch('my_module.__get_input')
def test_invalid_answer_remains_in_loop(self, mock):
"""
Test nothing's broken when answer is not ['Y', 'y', 'N', 'n']
"""
with self.assertRaises(SystemExit):
mock.side_effect = ['k', 'l', 'yeah', 'N']
# call to our function asking for input
あなたが試すことができるものを見るために 'pytest'の使用方法についてのチュートリアルを見ていましたか?これはかなり広い質問です。 – idjaw
@idjaw最近ではありません。以前はpytestを使っていましたが、ここで私のプロジェクトでTDDをやってみると、これは私の頭に浮かんできました。私はそれらのツイートをもう一度見てみましょう。 – Zelphir
あなたのテスト関数では、 'input()'関数を何か他のもの( "猿のパッチ"や "シャドウイング"とも呼ばれます)に割り当てることができます。 –