Обработка исключений в Python Behave Testing framework

Я думал о переключении с носа, чтобы вести себя для тестирования (мокка / чаи и т. Д. Испортили мне). Пока что так хорошо, но я, похоже, не могу найти способ тестирования для исключений, кроме того:

@then("It throws a KeyError exception") def step_impl(context): try: konfigure.load_env_mapping("baz", context.configs) except KeyError, e: assert (e.message == "No baz configuration found") 

С носом я могу аннотировать тест с помощью

  • Список всех тестов, найденных Nosetest
  • Могу ли я ограничить вывод покрытия носа в каталог (а не пакет)?
  • Изменение имен тестов, созданных генераторами носовых тестов
  • Проблемы с использованием носа в virtualenv
  •  @raises(KeyError) 

    Я не могу найти ничего подобного в поведении (не в источнике, не в примерах, не здесь). Конечно, было бы замечательно, если бы можно было указать исключения, которые могут быть выбраны в контурах сценария.

    Кто-нибудь был по этому пути?

  • Могу ли я ограничить вывод покрытия носа в каталог (а не пакет)?
  • Проблемы с использованием носа в virtualenv
  • Список всех тестов, найденных Nosetest
  • Изменение имен тестов, созданных генераторами носовых тестов
  • 4 Solutions collect form web for “Обработка исключений в Python Behave Testing framework”

    Я довольно новичок в BDD сам, но, как правило, идея заключалась бы в том, что тестовый документ, который ведет клиент, может ожидать, а не в реализациях шага. Поэтому я ожидаю, что канонический способ проверить это будет примерно таким:

     When I try to load config baz Then it throws a KeyError with message "No baz configuration found" 

    С шагами, определенными как:

     @when('...') def step(context): try: # do some loading here context.exc = None except Exception, e: context.exc = e @then('it throws a {type} with message "{msg}"') def step(context, type, msg): assert isinstance(context.exc, eval(type)), "Invalid exception - expected " + type assert context.exc.message == msg, "Invalid message - expected " + msg 

    Если это общий шаблон, вы можете просто написать свой собственный декоратор:

     def catch_all(func): def wrapper(context, *args, **kwargs): try: func(context, *args, **kwargs) context.exc = None except Exception, e: context.exc = e return wrapper @when('... ...') @catch_all def step(context): # do some loading here - same as before 

    Этот подход Try / catch работает Барри, но я вижу некоторые проблемы:

    • Добавление попытки / кроме ваших шагов означает, что ошибки будут скрыты.
    • Добавление дополнительного декоратора неэлегантно. Я бы хотел, чтобы мой декоратор был измененным.

    Мое предложение состоит в том, чтобы

    • иметь исключение ожиданий перед неудачным утверждением
    • в try / catch, повышать, если ошибка не ожидалась
    • в after_scenario, повышайте ошибку, если ожидаемая ошибка не найдена.
    • используйте модифицированные данные / когда / тогда всюду

    Код:

      def given(regexp): return _wrapped_step(behave.given, regexp) #pylint: disable=no-member def then(regexp): return _wrapped_step(behave.then, regexp) #pylint: disable=no-member def when(regexp): return _wrapped_step(behave.when, regexp) #pylint: disable=no-member def _wrapped_step(step_function, regexp): def wrapper(func): """ This corresponds to, for step_function=given @given(regexp) @accept_expected_exception def a_given_step_function(context, ... """ return step_function(regexp)(_accept_expected_exception(func)) return wrapper def _accept_expected_exception(func): """ If an error is expected, check if it matches the error. Otherwise raise it again. """ def wrapper(context, *args, **kwargs): try: func(context, *args, **kwargs) except Exception, e: #pylint: disable=W0703 expected_fail = context.expected_fail # Reset expected fail, only try matching once. context.expected_fail = None if expected_fail: expected_fail.assert_exception(e) else: raise return wrapper class ErrorExpected(object): def __init__(self, message): self.message = message def get_message_from_exception(self, exception): return str(exception) def assert_exception(self, exception): actual_msg = self.get_message_from_exception(exception) assert self.message == actual_msg, self.failmessage(exception) def failmessage(self, exception): msg = "Not getting expected error: {0}\nInstead got{1}" msg = msg.format(self.message, self.get_message_from_exception(exception)) return msg @given('the next step shall fail with') def expect_fail(context): if context.expected_fail: msg = 'Already expecting failure:\n {0}'.format(context.expected_fail.message) context.expected_fail = None util.show_gherkin_error(msg) context.expected_fail = ErrorExpected(context.text) 

    Я импортирую измененные данные / then / when вместо того, чтобы вести себя, и добавьте в мою среду environment.py, инициирующую контекст. Ожидаемый сбой перед сценарием и проверка его после:

      def after_scenario(context, scenario): if context.expected_fail: msg = "Expected failure not found: %s" % (context.expected_fail.message) util.show_gherkin_error(msg) 

    Выбранный try / except подход на самом деле абсолютно правильный, поскольку он показывает, как вы действительно используете код в реальной жизни. Однако есть причина, по которой вам это не совсем нравится. Это приводит к уродливым проблемам с такими вещами, как:

     Scenario: correct password accepted Given that I have a correct password When I attempt to log in Then I should get a prompt Scenario: correct password accepted Given that I have a correct password When I attempt to log in Then I should get an exception 

    Если я напишу определение шага без try / кроме второго, произойдет сбой. Если я напишу его с помощью try / except, то в первом сценарии риск будет скрывать исключение, особенно если исключение произойдет после того, как приглашение уже было напечатано.

    Вместо этого эти сценарии должны, ИМХО, быть написаны как нечто вроде

     Scenario: correct password accepted Given that I have a correct password When I log in Then I should get a prompt Scenario: correct password accepted Given that I have a correct password When I try to log in Then I should get an exception 

    Шаг «Я вхожу в систему» ​​не должен использовать try; «Я пытаюсь войти» подходит аккуратно, чтобы попытаться отдать тот факт, что может не быть успеха.

    Затем возникает вопрос о повторном использовании кода между двумя почти, но не совсем идентичными шагами. Вероятно, мы не хотим иметь две функции, которые оба входа. Помимо простой общей функции, которую вы вызываете, вы также можете сделать что-то вроде этого в конце вашего файла шагов.

     @when(u'{who} try to {what}') def step_impl(context): try: context.exception=None except Exception as e: context.exception=e 

    Это автоматически преобразует все шаги, содержащие слово «попробуйте», в шаги с тем же именем, но с попыткой удалить, а затем защитить их с помощью try / except.

    Есть несколько вопросов о том, когда вам действительно нужно иметь дело с исключениями в BDD, поскольку они не являются видимыми пользователем. Это не часть ответа на этот вопрос, хотя я поместил их в отдельную публикацию .

    Ведение дел не входит в бизнес-состязание утверждений. Поэтому он не дает для этого решения. Уже достаточно пакетов Python, которые решают эту проблему.

    СМ. ТАКЖЕ: behave.example: выберите библиотеку совпадений утверждений

    Python - лучший язык программирования в мире.