首页 > 解决方案 > 在输入提示下测试 python stdout

问题描述

以下方法的测试检查提示中的标准输出是否正确。

当 'input()' 被调用时,它会等待用户按下回车键并中断。测试通过自动按键“输入”来通过。

这很hacky,必须有更好的方法来测试这种方法。

方法

class GameDisplay:

@staticmethod
def prompt(text):
    input_value = input(text)
    return input_value

测试

from pynput.keyboard import Key, Controller

class TestGameDisplay(unittest.TestCase):

@patch('sys.stdout', new_callable=StringIO)
def test_prompt_output(self, mock_stdout):
    keyboard = Controller()
    keyboard.press(Key.enter)
    self.gameDisplay.prompt('Choose 0: ')
    keyboard.release(Key.enter)
    self.assertEqual( mock_stdout.getvalue(), 'Choose 0: ')

if __name__ == '__main__':
   unittest.main()

输出

由于按下回车而换行。

...................................
............................................
----------------------------------------------------------------------
Ran 79 tests in 0.113s

OK

测试修复尝试:

@patch('sys.stdout', new_callable=StringIO)
@patch('builtins.input', return_value='0')
def test_prompt_output(self, mock_stdout, input):
    self.gameDisplay.prompt('Choose 0: ')
    self.assertEqual( mock_stdout.getvalue(), 'Choose 0: ')

输出:

FAIL: test_prompt_output (tests.test_game_display.TestGameDisplay)
self.assertEqual( mock_stdout.getvalue(), 'Choose 0: ')
AssertionError: <MagicMock name='input.getvalue()' id='4675607744'> != 'Choose 0: '

----------------------------------------------------------------------
Ran 79 tests in 0.015s

FAILED (failures=1)

标签: pythonunit-testingmockingstdoutpynput

解决方案


mock_stdout 只捕获print语句的标准输出,所以添加print(text)

class GameDisplay:

    @staticmethod
    def prompt(text):
        input_value = input(text)
        print(text)
        return input_value

这段代码经过测试并按预期工作

@mock.patch('sys.stdout', new_callable=StringIO)
@mock.patch('__builtin__.input', return_value='0')
def test_prompt_output3(self, mock_input, mock_stdout):
        p = GameDisplay.prompt('Choose 0: ')
        self.assertEqual(p, '0')
        self.assertEqual(mock_stdout.getvalue(), 'Choose 0: \n')

但随后它会打印选择 0: 两次

在现实生活中测试 return_valueprompt()应该足够了。

如果您真的想同时模拟输入和输出,则只剩下 1 个选项。

这会做,只要你不同时模拟print

print(text)
input_value = input()

或者采取一种疯狂的方式,伪造标准输出:

mock_stdout = StringIO('Choose 0: ')
p = GameDisplay.prompt('Choose 0: ')
self.assertEqual(mock_stdout.getvalue(), 'Choose 0: ')

或者这类似于将 return_value 分配给模拟对象:

with mock.patch('sys.stdout', new=StringIO("xxx")) as mock_stdout:

推荐阅读