python在unittests中模拟原始输入
假设我有以下python代码:python在unittests中模拟原始输入,python,unit-testing,mocking,Python,Unit Testing,Mocking,假设我有以下python代码: def answer(): ans = raw_input('enter yes or no') if ans == 'yes': print 'you entered yes' if ans == 'no': print 'you entered no' 如何为此编写单元测试?我知道我必须用Mock,但我不明白怎么用。有人能举个简单的例子吗?您不能修补输入,但可以将其包装为使用mock.patch()。以
def answer():
ans = raw_input('enter yes or no')
if ans == 'yes':
print 'you entered yes'
if ans == 'no':
print 'you entered no'
如何为此编写单元测试?我知道我必须用Mock,但我不明白怎么用。有人能举个简单的例子吗?您不能修补输入,但可以将其包装为使用mock.patch()。以下是一个解决方案:
def answer():
ans = raw_input('enter yes or no')
if ans == 'yes':
return 'you entered yes'
if ans == 'no':
return 'you entered no'
def test_answer_yes():
assert(answer() == 'you entered yes')
def test_answer_no():
assert(answer() == 'you entered no')
origin_raw_input = __builtins__.raw_input
__builtins__.raw_input = lambda x: "yes"
test_answer_yes()
__builtins__.raw_input = lambda x: "no"
test_answer_no()
__builtins__.raw_input = origin_raw_input
from unittest.mock import patch
from unittest import TestCase
def get_input(text):
return input(text)
def answer():
ans = get_input('enter yes or no')
if ans == 'yes':
return 'you entered yes'
if ans == 'no':
return 'you entered no'
class Test(TestCase):
# get_input will return 'yes' during this test
@patch('yourmodule.get_input', return_value='yes')
def test_answer_yes(self, input):
self.assertEqual(answer(), 'you entered yes')
@patch('yourmodule.get_input', return_value='no')
def test_answer_no(self, input):
self.assertEqual(answer(), 'you entered no')
请记住,这段代码只适用于Python版本3.3+好的,首先,我觉得有必要指出,在所讨论的原始代码中,实际上有两件事情需要解决:
原始输入
(输入副作用)需要模拟打印
(输出副作用)需要检查unittest
模块获得了模拟和检查副作用的能力。但是,截至2014年初,只有30%的Python程序员使用了3.x,因此为了其他70%仍在使用2.x的Python程序员,我将概述一个答案。按照目前的速度,3.x在2019年之前不会超过2.x,2.x在2027年之前不会消失。因此,我认为这个答案在未来几年将是有用的
我想一次一个地解决上面列出的问题,因此我将首先将您的函数从使用print
作为其输出更改为使用return
。毫不奇怪,以下是代码:
def answerReturn():
ans = raw_input('enter yes or no')
if ans == 'yes':
return 'you entered yes'
if ans == 'no':
return 'you entered no'
所以我们需要做的就是模拟原始输入。非常简单-向我们展示了如何通过模拟实现快速删除\uuu内置\uuuu.raw\u输入
实现。除了他的答案没有正确地组织成一个测试用例和函数,所以我将对此进行演示
import unittest
class TestAnswerReturn(unittest.TestCase):
def testYes(self):
original_raw_input = __builtins__.raw_input
__builtins__.raw_input = lambda _: 'yes'
self.assertEqual(answerReturn(), 'you entered yes')
__builtins__.raw_input = original_raw_input
def testNo(self):
original_raw_input = __builtins__.raw_input
__builtins__.raw_input = lambda _: 'no'
self.assertEqual(answerReturn(), 'you entered no')
__builtins__.raw_input = original_raw_input
关于Python命名约定的小提示——解析器需要但未使用的变量通常被命名为。
,就像lambda未使用的变量一样(在原始输入
的情况下,这通常是向用户显示的提示,以防您想知道为什么在这种情况下需要它)
无论如何,这是混乱和多余的。因此,我将通过添加contextmanager
来消除重复,这将允许使用语句实现简单的
from contextlib import contextmanager
@contextmanager
def mockRawInput(mock):
original_raw_input = __builtins__.raw_input
__builtins__.raw_input = lambda _: mock
yield
__builtins__.raw_input = original_raw_input
class TestAnswerReturn(unittest.TestCase):
def testYes(self):
with mockRawInput('yes'):
self.assertEqual(answerReturn(), 'you entered yes')
def testNo(self):
with mockRawInput('no'):
self.assertEqual(answerReturn(), 'you entered no')
我认为这很好地回答了第一部分的问题。转到第二部分-检查打印。我发现这要复杂得多——我很想听听是否有人有更好的答案
无论如何,print
语句不能被重写,但是如果您使用print()
函数(您应该这样做)和from\uuuuuuu future\uuuuuuu import print\u函数
可以使用以下功能:
class PromiseString(str):
def set(self, newString):
self.innerString = newString
def __eq__(self, other):
return self.innerString == other
@contextmanager
def getPrint():
promise = PromiseString()
original_print = __builtin__.print
__builtin__.print = lambda message: promise.set(message)
yield promise
__builtin__.print = original_print
class TestAnswer(unittest.TestCase):
def testYes(self):
with mockRawInput('yes'), getPrint() as response:
answer()
self.assertEqual(response, 'you entered yes')
def testNo(self):
with mockRawInput('no'), getPrint() as response:
answer()
self.assertEqual(response, 'you entered no')
这里需要技巧的一点是,在输入with
块之前,您需要生成
响应。但是在调用with
块中的print()
之前,您无法知道该响应是什么。如果字符串是可变的,这将很好,但它们不是。因此,取而代之的是一个小的promise或proxy类-PromiseString
。它只做两件事——允许设置一个字符串(或者任何东西),并让我们知道它是否等于另一个字符串。PromiseString
是yield
ed,然后设置为with
块中通常为print
的值
希望你们能欣赏我写下的所有这些诡计,因为今晚我花了大约90分钟才完成。我测试了所有这些代码,并用Python 2.7验证了它们是否都能正常工作。遇到了同样的问题,但我只是模拟了\uuuuuuuuu内置的.raw\uu输入
仅在Python2上测试<代码>pip安装模拟
如果尚未安装软件包
from mock import patch
from unittest import TestCase
class TestAnswer(TestCase):
def test_yes(self):
with patch('__builtin__.raw_input', return_value='yes') as _raw_input:
self.assertEqual(answer(), 'you entered yes')
_raw_input.assert_called_once_with('enter yes or no')
def test_no(self):
with patch('__builtin__.raw_input', return_value='no') as _raw_input:
self.assertEqual(answer(), 'you entered no')
_raw_input.assert_called_once_with('enter yes or no')
或者,使用库,您可以简化两个测试:
from genty import genty, genty_dataset
from mock import patch
from unittest import TestCase
@genty
class TestAnswer(TestCase):
@genty_dataset(
('yes', 'you entered yes'),
('no', 'you entered no'),
)
def test_answer(self, expected_input, expected_answer):
with patch('__builtin__.raw_input', return_value=expected_input) as _raw_input:
self.assertEqual(answer(), expected_answer)
_raw_input.assert_called_once_with('enter yes or no')
我使用的是Python3.4,必须修改上面的答案。我的解决方案将常见代码分解到自定义的runTest
方法中,并向您展示如何修补input()
和print()
。以下是广告中的代码:
import unittest
from io import StringIO
from unittest.mock import patch
def answer():
ans = input('enter yes or no')
if ans == 'yes':
print('you entered yes')
if ans == 'no':
print('you entered no')
class MyTestCase(unittest.TestCase):
def runTest(self, given_answer, expected_out):
with patch('builtins.input', return_value=given_answer), patch('sys.stdout', new=StringIO()) as fake_out:
answer()
self.assertEqual(fake_out.getvalue().strip(), expected_out)
def testNo(self):
self.runTest('no', 'you entered no')
def testYes(self):
self.runTest('yes', 'you entered yes')
if __name__ == '__main__':
unittest.main()
以下是我在Python 3中所做的工作:
类MockInputFunction:
def uu init uu(self,return_value=None):
self.return\u value=返回值
self.\u orig\u input\u fn=\uuuuuu内置\uuuuu['input']
定义模拟输入(自身,提示):
打印(提示+str(自身返回值))
返回self.return\u值
定义输入(自我):
__内置\uuu['input']=self.\u mock\u input\u fn
定义退出(自身、类型、值、回溯):
__内置\uuu['input']=self.\u orig\u input\u fn
然后可以在任何上下文中使用。例如,使用普通的assert
语句
from contextlib import contextmanager
@contextmanager
def mockRawInput(mock):
original_raw_input = __builtins__.raw_input
__builtins__.raw_input = lambda _: mock
yield
__builtins__.raw_input = original_raw_input
class TestAnswerReturn(unittest.TestCase):
def testYes(self):
with mockRawInput('yes'):
self.assertEqual(answerReturn(), 'you entered yes')
def testNo(self):
with mockRawInput('no'):
self.assertEqual(answerReturn(), 'you entered no')
def func():
“”“要测试的函数”“”
x=输入(“x是什么?”)
返回整数(x)
#要进行测试,您只需执行以下操作:
使用MockInputFunction(返回值=13):
assert func()==13
可能重复的我找不到答案,这三个答案中有一个是关于使用mock
测试raw\u输入的@ArtOfWarfare mock在python3.3中是新的,您应该在答案中指定python版本。谢谢@gawell您不需要包装输入@patch('builtins.input',return_value='yes')
应该可以做到这一点。@patch('builtins.input',return_value='yes')
不是一个好主意,因为它会导致pdb.set_trace()
崩溃。为什么需要在def test\u answer\u yes(self,input)中添加输入作为第二个参数:
?我使用nose2
(不直接使用unittest
)调整了您的答案以适应我的设置,这对我很有效。需要注意的一点是,如果您更改fakeout.ge