Как издеваться над методом 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):
    #
    # 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)

Я использую Макет Майкла Фурда и нос для запуска тестов.

Когда я запускаю тест, я получаю:

  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 (). Есть ли способ проверить код выхода?

Кстати, тест будет более надежным, если я добавлю:

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

Первый assertEquals в вашем обновлении 2 не нужен, поскольку строка «кроме» над ним будет перехватывать только исключения SystemExit.

rbp 20.04.2010 23:42
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
15
1
4 189
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Будет ли это работать вместо assertEquals?

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

Это должно перехватить исключение SystemExit и предотвратить завершение сценария.

Я "не принял" ваш ответ, так как он не совсем правильный. Но поскольку вы дали мне понимание, которое привело меня к ответу (см. Обновления вопроса), я дам вам пару дней, чтобы отредактировать ваш ответ, а затем я приму.

Daryl Spitzer 09.01.2009 23:58

Вероятно, в этом вопросе содержится какая-то новая информация:

Java: как проверить методы, вызывающие System.exit ()?

Я быстро прочитал и, хотя это та же проблема (в Java), я не нашел здесь ничего применимого (для Python). Ты?

Daryl Spitzer 09.01.2009 21:10
Ответ принят как подходящий

Как отмечалось в моих обновлениях на мой вопрос, мне пришлось изменить ответ dF на:

self.assertRaises(SystemExit, sut.main)

... и я придумал несколько более длинных фрагментов для проверки кода выхода.

[Примечание: я принял свой ответ, но удалю этот ответ и приму dF, если он обновит свой.]

Добавьте @raises в свою тестовую функцию. То есть:

from nose.tools import raises

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

Другие вопросы по теме