2015-01-12 4 views
6

Я думал о переключении с носа, чтобы вести себя для тестирования (мокка/chai и т. Д. Испортили мне). До сих пор так хорошо, но я не могу показаться, чтобы выяснить какой-либо способ тестирования исключений, кроме:Обработка Исключения в Python Behave Тестирование рамки

@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") 

С носом я могу аннотирования тест с

@raises(KeyError) 

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

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

+0

Мне кажется, что обеспечение того, чтобы код выдавал определенные исключения в определенных сценариях, является довольно стандартной проверкой. Также полезно показать код клиента, какое поведение он может ожидать. Когда я проверяю это, я не проверю тесты! В любом случае это довольно стандартная функция большинства тестовых фреймворков. –

ответ

6

Я довольно новичок в 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 
+0

Работал, спасибо! Вчера вечером мой мозг был ржавым. –

+0

Раньше отказ от поведения. Наличие отдельного файла спецификации огурца и соглашение, которое заставляет почти все быть многоэтапным тестом, - слишком большая работа. Я обнаружил, что это разрушает мой поток развития. Вернемся к носу! –

0

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

ТАКЖЕ:behave.example: Select an assertion matcher library

+0

Hi @jenisys; Спасибо за поведение BTW Я думаю, что проблема здесь не только в подборах. Непонятно, когда вы запускаете шаг «Когда», на что вы должны совпадать. например если вы соглашаетесь на исключение, но не проверяете его, можно позже замаскировать другое исключение. Если вы получаете исключение на этапе «когда», оно должно распространяться как тестовая ошибка. – Michael

2

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

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

Мое предложение

  • есть ожидать исключение до неисправного заявления
  • в Try/уловом, воскрешал, если ошибка не ожидается
  • в 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) 

импортировать мой модифицирована с учетом/то /, когда вместо себя вести, и добавить к моему 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) 
1

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

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/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 

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

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

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

Это будет автоматически конвертировать все шаги, содержащие слово «попытаться» на шаги с тем же именем, но с ПЫТАЮТСЯ удалены, а затем защитить их с Try/за исключением.

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

Смежные вопросы