Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/322.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
python在unittests中模拟原始输入_Python_Unit Testing_Mocking - Fatal编程技术网

python在unittests中模拟原始输入

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()。以

假设我有以下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':
        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+

好的,首先,我觉得有必要指出,在所讨论的原始代码中,实际上有两件事情需要解决:

  • 原始输入
    (输入副作用)需要模拟
  • 打印
    (输出副作用)需要检查
  • 在单元测试的理想功能中,不会有任何副作用。一个函数只需通过提交参数进行测试,并检查其输出。但是,我们经常想要测试不理想的函数,例如,在像您这样的函数中

    那我们该怎么办?在Python3.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