Linux 如何模拟pyunit的stdin输入?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6271947/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 04:24:02  来源:igfitidea点击:

How can I simulate input to stdin for pyunit?

pythonlinuxpython-unittest

提问by bejar37

I'm trying to test a function that takes input from stdin, which I'm currently testing with something like this:

我正在尝试测试一个从 获取输入的函数,stdin我目前正在使用以下内容进行测试:

cat /usr/share/dict/words | ./spellchecker.py

In the name of test automation, is there any way that pyunitcan fake input to raw_input()?

以测试自动化的名义,有什么方法pyunit可以伪造输入raw_input()

采纳答案by Johnsyweb

The short answer is to monkey patchraw_input().

简短的回答是猴子补丁raw_input()

There are some good examples in the answer to How to display the redirected stdin in Python?

How to display the redirected stdin in Python?的答案中有一些很好的例子

Here is a simple, trivial example using a lambdathat throws away the prompt and returns what we want.

这是一个简单的示例,使用 alambda丢弃提示并返回我们想要的内容。

System Under Test

被测系统

cat ./name_getter.py
#!/usr/bin/env python

class NameGetter(object):

    def get_name(self):
        self.name = raw_input('What is your name? ')

    def greet(self):
        print 'Hello, ', self.name, '!'

    def run(self):
        self.get_name()
        self.greet()

if __name__ == '__main__':
    ng = NameGetter()
    ng.run()

$ echo Derek | ./name_getter.py 
What is your name? Hello,  Derek !

Test case:

测试用例:

$ cat ./t_name_getter.py
#!/usr/bin/env python

import unittest
import name_getter

class TestNameGetter(unittest.TestCase):

    def test_get_alice(self):
        name_getter.raw_input = lambda _: 'Alice'
        ng = name_getter.NameGetter()
        ng.get_name()
        self.assertEquals(ng.name, 'Alice')

    def test_get_bob(self):
        name_getter.raw_input = lambda _: 'Bob'
        ng = name_getter.NameGetter()
        ng.get_name()
        self.assertEquals(ng.name, 'Bob')

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

$ ./t_name_getter.py -v
test_get_alice (__main__.TestNameGetter) ... ok
test_get_bob (__main__.TestNameGetter) ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

回答by Dunes

Update -- using unittest.mock.patch

更新——使用 unittest.mock.patch

Since python 3.3 there is new submodule for unittestcalled mock that does exactly what you need to do. For those using python 2.6 or above there is a backport of mockfound here.

从 python 3.3 开始,有一个unittest名为 mock 的新子模块,它可以完全满足您的需求。对于使用Python 2.6或以上的有一个反向移植mock发现了这里

import unittest
from unittest.mock import patch

import module_under_test


class MyTestCase(unittest.TestCase):

    def setUp(self):
        # raw_input is untouched before test
        assert module_under_test.raw_input is __builtins__.raw_input

    def test_using_with(self):
        input_data = "123"
        expected = int(input_data)

        with patch.object(module_under_test, "raw_input", create=True, 
                return_value=expected):
            # create=True is needed as raw_input is not in the globals of 
            # module_under_test, but actually found in __builtins__ .
            actual = module_under_test.function()

        self.assertEqual(expected, actual)

    @patch.object(module_under_test, "raw_input", create=True)
    def test_using_decorator(self, raw_input):
        raw_input.return_value = input_data = "123"
        expected = int(input_data)

        actual = module_under_test.function()

        self.assertEqual(expected, actual)

    def tearDown(self):
        # raw input is restored after test
        assert module_under_test.raw_input is __builtins__.raw_input

if __name__ == "__main__":
    unittest.main()
# where module_under_test.function is:
def function():
    return int(raw_input("prompt> "))

Previous answer -- replacing sys.stdin

上一个答案——替换 sys.stdin

I think the sys module might be what you're looking for.

我认为 sys 模块可能就是你要找的。

You can do something like

你可以做类似的事情

import sys

# save actual stdin in case we need it again later
stdin = sys.stdin

sys.stdin = open('simulatedInput.txt','r') 
# or whatever else you want to provide the input eg. StringIO

raw_input will now read from simulatedInput.txt whenever it is called. If the contents of simulatedInput was

raw_input 现在会在被调用时从simulatedInput.txt 中读取。如果模拟输入的内容是

hello
bob

then the first call to raw_input would return "hello", the second "bob" and third would throw an EOFError as there was no more text to read.

那么对 raw_input 的第一次调用将返回“hello”,第二个“bob”和第三个将抛出 EOFError 因为没有更多的文本可供阅读。

回答by Michael Kent

Replace sys.stdinwith an instance of StringIO, and load the StringIOinstance with the data you want returned via sys.stdin. Also, sys.__stdin__contains the original sys.stdinobject, so restoring sys.stdinafter your test is as simple as sys.stdin = sys.__stdin__.

替换sys.stdin为 的实例StringIO,并StringIO使用要通过 返回的数据加载该实例sys.stdin。此外,sys.__stdin__包含原始sys.stdin对象,因此sys.stdin在测试后恢复就像sys.stdin = sys.__stdin__.

Fudgeis a great python mock module, with convenient decorators for doing patching like this for you, with automatic cleanup. You should check it out.

Fudge是一个很棒的 Python 模拟模块,带有方便的装饰器,可以为您进行这样的修补,并具有自动清理功能。你应该检查一下。

回答by Pavel Repin

You didn't describe what sort of code is in spellchecker.py, which gives me freedom to speculate.

你没有描述 什么类型的代码spellchecker.py,这让我可以自由地推测。

Suppose it's something like this:

假设它是这样的:

import sys

def check_stdin():
  # some code that uses sys.stdin

To improve testability of check_stdinfunction, I propose to refactor it like so:

为了提高check_stdin功能的可测试性,我建议像这样重构它:

def check_stdin():
  return check(sys.stdin)

def check(input_stream):
  # same as original code, but instead of
  # sys.stdin it is written it terms of input_stream.

Now most of your logic is in checkfunction, and you can hand-craft whatever input you can imagine in order to test it properly, without any need to deal with stdin.

现在您的大部分逻辑都已check运行,您可以手工制作您能想到的任何输入以正确测试它,而无需处理任何stdin.

My 2 cents.

我的 2 美分。

回答by Serge

If you are using mock module(written by Michael Foord), in order to mock raw_inputfunction you can use syntax like:

如果您使用的是模拟模块(由 Michael Foord 编写),为了模拟raw_input函数,您可以使用如下语法:

@patch('src.main.raw_input', create=True, new=MagicMock(return_value='y'))
def test_1(self):
    method_we_try_to_test();    # method or function that calls **raw_input**