Как мне высмеять метод Python OptionParser.error(), который делает sys.exit()?
Я пытаюсь выполнить unit test код, который выглядит следующим образом:
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() естественно полагается на это. Но нос или unittest обнаруживает (ожидаемый) sys.exit(2) и не проходит тест.
Я могу сделать тестовый проход, добавив "return 2" под вызовом parser.error() в main() и удалив вызов sys.exit() из parser_error_mock(), но мне неловко изменить код чтобы пройти тест. Есть ли лучшее решение?
Обновить: df работает, хотя правильный вызов - "self.assertRaises(SystemExit, sut.main)".
Это означает, что тест проходит независимо от числа в sys.exit() в parser_error_mock(). Есть ли способ проверить код выхода?
BTW, тест более надежный, если я добавлю:
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')
Ответы
Ответ 1
Как отмечалось в моих обновлениях к моему вопросу, мне пришлось изменить dF ответ на:
self.assertRaises(SystemExit, sut.main)
... и я придумал несколько более длинных фрагментов, чтобы проверить код выхода.
[Примечание: я принял свой собственный ответ, но я удалю этот ответ и приму его dF, если он обновит его.]
Ответ 2
Будет ли это работать вместо assertEquals
?
self.assertRaises(SystemExit, sut.main, 2)
Это должно уловить исключение SystemExit
и предотвратить завершение script.
Ответ 3
Вероятно, этот вопрос содержит некоторую новую информацию:
Java: как тестировать методы, которые вызывают System.exit()?
Ответ 4
Добавьте @raises
в свою тестовую функцию. То есть:
from nose.tools import raises
@raises(SystemExit)
@patch('optparse.OptionParser')
def test_main_with_missing_p4clientsdir_option(self, mock_optionparser):
# Setup
...
sut.main()