如何模拟Python方法OptionParser.error(),该方法执行sys.exit()操作?
如何模拟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')
这个问题的出现是因为用户想要模拟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()操作。
希望这个解决方法对你有帮助!