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
How can I simulate input to stdin for pyunit?
提问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 pyunit
can 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 lambda
that 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 unittest
called mock that does exactly what you need to do. For those using python 2.6 or above there is a backport of mock
found 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.stdin
with an instance of StringIO
, and load the StringIO
instance with the data you want returned via sys.stdin
. Also, sys.__stdin__
contains the original sys.stdin
object, so restoring sys.stdin
after 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_stdin
function, 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 check
function, 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**