如何模拟执行 sys.exit() 的 Python 方法 OptionParser.error()?

发布于 2024-07-11 16:06:52 字数 2625 浏览 8 评论 0 原文

我正在尝试对一些如下所示的代码进行单元测试:

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):
    #
    # setup
    #
    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

    #
    # exercise & verify
    #
    self.assertEquals(sut.main(), 2)

我正在使用 Michael Foord 的 Mock,并运行测试。

当我运行测试时,我得到:

  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')

I'm trying to unit test some code that looks like this:

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

With code that looks like this:

@patch('optparse.OptionParser')
def test_main_with_missing_p4clientsdir_option(self, mock_optionparser):
    #
    # setup
    #
    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

    #
    # exercise & verify
    #
    self.assertEquals(sut.main(), 2)

I'm using Michael Foord's Mock, and nose to run the tests.

When I run the test, I get:

  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)

The problem is that OptionParser.error does a sys.exit(2), and so main() naturally relies on that. But nose or unittest detects the (expected) sys.exit(2) and fails the test.

I can make the test pass by adding "return 2" under the parser.error() call in main() and removing the sys.exit() call from parser_error_mock(), but I find it distasteful to modify the code under test to allow a test to pass. Is there a better solution?

Update: df's answer works, although the correct call is "self.assertRaises(SystemExit, sut.main)".

Which means the test passes whatever the number is in the sys.exit() in parser_error_mock(). Is there any way to test for the exit code?

BTW, the test is more robust if I add:

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

at the end.

Update 2: I can test for the exit code by replacing "self.assertRaises(SystemExit, sut.main)" with:

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')

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

甜妞爱困 2024-07-18 16:06:52

这可以代替 assertEquals 吗?

self.assertRaises(SystemExit, sut.main, 2)

这应该捕获 SystemExit 异常并防止脚本终止。

Will this work instead of assertEquals?

self.assertRaises(SystemExit, sut.main, 2)

This should catch the SystemExit exception and prevent the script from terminating.

李白 2024-07-18 16:06:52

正如我对问题的更新中所指出的,我必须修改 dF 的答案:

self.assertRaises(SystemExit, sut.main)

......然后我想到了用一些更长的代码片段来测试退出代码。

[注意:我接受了自己的答案,但如果他更新了他的答案,我将删除此答案并接受 dF。]

As noted in my updates to my question, I had to modify dF's answer to:

self.assertRaises(SystemExit, sut.main)

...and I came up with a few longer snippet to test for the exit code.

[Note: I accepted my own answer, but I will delete this answer and accept dF's if he updates his.]

慕巷 2024-07-18 16:06:52

可能这个问题包含一些新信息:

Java:如何测试方法调用 System.exit()?

Probably this question contains some new information:

Java: How to test methods that call System.exit()?

再浓的妆也掩不了殇 2024-07-18 16:06:52

@raises 添加到您的测试函数中。 IE:

from nose.tools import raises

@raises(SystemExit)
@patch('optparse.OptionParser')
def test_main_with_missing_p4clientsdir_option(self, mock_optionparser):
    # Setup
    ...
    sut.main()

Add @raises to your test function. I.e:

from nose.tools import raises

@raises(SystemExit)
@patch('optparse.OptionParser')
def test_main_with_missing_p4clientsdir_option(self, mock_optionparser):
    # Setup
    ...
    sut.main()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文