如何模拟Python方法OptionParser.error(),该方法执行sys.exit()操作?

7 浏览
0 Comments

如何模拟Python方法OptionParser.error(),该方法执行sys.exit()操作?

我正在尝试对以下代码进行单元测试:

def main():
    parser = optparse.OptionParser(description='This tool is cool', prog='cool-tool')
    parser.add_option('--foo', action='store', help='The foo option is self-explanatory')
    options, arguments = parser.parse_args()
    if not options.foo:
        parser.error('--foo option is required')
    print "Your foo is %s." % options.foo
    return 0
if __name__ == '__main__':
   sys.exit(main())

使用以下代码:

@patch('optparse.OptionParser')
def test_main_with_missing_p4clientsdir_option(self, mock_optionparser):
    #
    # 设置
    #
    optionparser_mock = Mock()
    mock_optionparser.return_value = optionparser_mock
    options_stub = Mock()
    options_stub.foo = None
    optionparser_mock.parse_args.return_value = (options_stub, sentinel.arguments)
    def parser_error_mock(message):
        self.assertEquals(message, '--foo option is required')
        sys.exit(2)
    optionparser_mock.error = parser_error_mock
    #
    # 执行 & 验证
    #
    self.assertEquals(sut.main(), 2)

我正在使用Michael Foord's Mock和nose来运行测试。

当我运行测试时,我得到以下结果:

  File "/Users/dspitzer/Programming/Python/test-optparse-error/tests/sut_tests.py", line 27, in parser_error_mock
    sys.exit(2)
SystemExit: 2
----------------------------------------------------------------------
Ran 1 test in 0.012s
FAILED (errors=1)

问题在于OptionParser.error执行了sys.exit(2),因此main()自然依赖于它。但是nose或unittest检测到了(预期的)sys.exit(2),并且导致测试失败。

我可以通过在main()中parser.error()调用下添加"return 2"并从parser_error_mock()中删除sys.exit()调用来使测试通过,但我觉得修改被测试的代码以使测试通过是不好的。有没有更好的解决方案?

更新df的答案有效,尽管正确的调用是"self.assertRaises(SystemExit, sut.main)"。

这意味着测试无论在parser_error_mock()中sys.exit()的数字是多少都会通过。有没有办法测试退出代码?

顺便说一下,如果我在最后添加以下内容,测试更健壮:

self.assertEquals(optionparser_mock.method_calls, [('add_option', ('--foo',), {'action': 'store', 'help': 'The foo option is self-explanatory'}), ('parse_args', (), {})])

更新2:我可以通过将"self.assertRaises(SystemExit, sut.main)"替换为以下内容来测试退出代码:

try:
    sut.main()
except SystemExit, e:
    self.assertEquals(type(e), type(SystemExit()))
    self.assertEquals(e.code, 2)
except Exception, e:
    self.fail('unexpected exception: %s' % e)
else:
    self.fail('SystemExit exception expected')

0
0 Comments

这个问题的出现是因为用户想要模拟Python方法OptionParser.error(),该方法会执行sys.exit()操作。用户在问题中提到了一个与之相关的Java问题,即如何测试调用System.exit()的方法。虽然这个Java问题与Python的问题类似,但它并没有提供适用于Python的解决方法。

解决这个问题的方法是使用Python的mock库来模拟OptionParser.error()方法,并验证sys.exit()是否被调用。下面是一种可能的解决方法:

import sys
from argparse import ArgumentParser
from unittest import TestCase
from unittest.mock import patch
def my_option_parser():
    parser = ArgumentParser()
    parser.add_argument("-f", "--file", dest="filename", help="input file")
    return parser
def my_function():
    parser = my_option_parser()
    args = parser.parse_args()
    
    if not args.filename:
        parser.error("File name is required")
        sys.exit(1)
    
    # Other code here
        
class MyTestCase(TestCase):
    @patch('sys.exit')
    def test_my_function(self, mock_exit):
        with patch('argparse.ArgumentParser.error') as mock_error:
            my_function()
            self.assertTrue(mock_error.called)
            mock_exit.assert_called_with(1)
if __name__ == '__main__':
    MyTestCase().test_my_function()

在这个例子中,我们首先定义了一个自定义的OptionParser方法my_option_parser(),它返回一个包含一个必需参数的ArgumentParser对象。然后,在my_function()中,我们使用这个自定义的OptionParser来解析命令行参数,并在参数缺失时调用parser.error()和sys.exit(1)。

为了测试my_function()中的逻辑,我们使用了Python的mock库。使用@patch装饰器,我们可以模拟sys.exit()方法,并在测试中验证它是否被调用。我们还使用with语句结合patch来模拟parser.error()方法,并验证它是否被调用。

通过这种方式,我们可以模拟OptionParser.error()方法并对其进行测试,而不会实际执行sys.exit()操作。

希望这个解决方法对你有帮助!

0
0 Comments

问题的原因是:作者在测试Python的OptionParser.error()方法时,该方法会调用sys.exit(),导致测试代码无法正常运行。

解决方法是:作者修改了dF的答案,使用self.assertRaises(SystemExit, sut.main)来测试代码中的sys.exit()。并且作者还编写了一段较长的代码片段来测试退出代码。

作者提到,他已经接受了自己的答案,但如果dF更新了他的答案,作者将删除自己的答案并接受dF的答案。

0
0 Comments

问题的出现原因是要对Python中的OptionParser.error()方法进行模拟(mock),该方法在执行时会调用sys.exit(),导致脚本终止。解决方法是使用self.assertRaises(SystemExit, sut.main, 2)来捕获SystemExit异常,从而阻止脚本终止。

0