【问题标题】:Testing python stdout on input prompt在输入提示下测试 python stdout
【发布时间】:2018-10-27 01:12:24
【问题描述】:

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

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

【问题讨论】:

  • 尝试交换装饰器的顺序?
  • @AlexHall 交换装饰器时得到以下输出:AssertionError: '' != 'Choose 0: '
  • 也许只是单独打印提示,然后在没有提示的情况下使用input()?
  • 这可以工作,但必须分别调用两个函数才能执行一项任务。
  • 交换装饰器是朝着正确方向迈出的一步。然后,您还必须使用打印到标准输出的内容来修补 input。原始输入没有被调用,所以没有任何东西可以打印到标准输出,因此它的值是空字符串。

标签: python unit-testing mocking stdout pynput


【解决方案1】:

mock_stdout 只捕获print 语句的stdout,所以添加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: 两次

测试prompt()的return_value在现实生活中应该够用了。

如果您真的想同时模拟输入和输出,则只剩下 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:

【讨论】:

  • 这是一个不错的选择,但它会打印两次Choose 0: 。一次是调用input(),第二次是调用print()。
  • @Huascar,这在现实生活中是不合理的,我添加了一个虚假的一切答案。
猜你喜欢
  • 2013-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-13
  • 1970-01-01
  • 1970-01-01
  • 2017-11-12
相关资源
最近更新 更多