正确 'continue' 行为的单元测试

Unittesting for correct 'continue' behaviour

我有一个通过提示要求用户确认的功能。它接受 y 或 n 作为答案,否则它会再次询问。

现在,我想为这个函数写一个单元测试。我可以很好地测试 y 或 n 的正确行为,但是 我如何测试我的函数是否正确拒绝不可接受的输入?

这是 foo.py 的代码:

def get_input(text):
    """gets console input and returns it; needed for mocking during unittest
    """
    return input(text)


def confirm(message='Confirm?', default=False):
    """prompts for yes or no response from the user. Returns True for yes and
    False for no.
    'default' should be set to the default value assumed by the caller when
    user simply types ENTER, and is marked in the prompt with square brackets.
    """
    if default:
        message = '%s [y]|n: ' % (message) # default answer = yes
    else:
        message = '%s y|[n]: ' % (message) # default answer = no

    while True:
        answer = get_input(message).lower()

        if not answer:
            return default
        if answer not in ['y', 'n']:
            print('Please enter y or n!')
            continue
        if answer == "y":
            return True
        if answer == 'n':
            return False   

answer = confirm()
print(answer)

这是我的测试 class:

import unittest
import foo

class TestFoo_confirm(unittest.TestCase):
    """testing confirm function
    """
    @unittest.mock.patch('foo.get_input', return_value='y')
    def test_answer_yes(self, _):
        self.assertEqual(foo.confirm(), True) # confirmed if 'y' was entered

那么,如何为“1”之类的输入值编写类似的测试(或者我需要如何调整 confirm() 函数以使其可测试)? 目前,如果我从 unittest 文件中调用 foo.confirm(),它只会陷入无限循环,而不会 return 任何东西。 (我明白为什么会这样,只是不知道如何规避它。)

有什么想法吗?

你可以试试这个:

import unittest, unittest.mock
import foo

class TestFoo_confirm(unittest.TestCase):
    """testing confirm function
    """
    @unittest.mock.patch('foo.get_input', return_value='y')
    def test_answer_yes(self, _):
        self.assertEqual(foo.confirm(), True) # confirmed if 'y' was entered

    @unittest.mock.patch('builtins.print')
    @unittest.mock.patch('foo.get_input', side_effect=['1','yn','yes','y']) # this will make the mock return '1', 'yn' and so on in sequence
    def test_invalid_answer(self, mock_input, mock_print):
        self.assertEqual(foo.confirm(), True) # it should eventually return True
        self.assertEqual(mock_input.call_count, 4) # input should be called four times
        mock_print.assert_called_with('Please enter y or n!')

在第二个测试用例中,我们模拟一个用户输入了三个无效输入,在再次提示后,最终输入了'y'。所以我们修补 foo.get_input 的方式是 returns 1 第一次被调用,然后是 yn,然后是 yes,最后是 y .前三个示例应该使确认功能再次提示用户。我还修补了打印功能,以便 'Please enter y or n!' 消息在测试时不会出现。这不是必需的。

然后我们断言我们的模拟输入被调用了四次,这意味着前三次,确认函数被重新提示。

最后我们断言打印函数被调用(至少一次)'Please enter y or n!'。

这不会测试打印语句的数量是否正确或它们的顺序是否正确,但我怀疑这也是可能的