Подтвердить что ты не робот

Как заставить SpecFlow ожидать исключения?

Я использую SpecFlow, и я бы хотел написать такой сценарий, как:

Scenario: Pressing add with an empty stack throws an exception
    Given I have entered nothing into the calculator
    When I press add
    Then it should throw an exception

It calculator.Add(), который собирается генерировать исключение, так как я могу справиться с этим в методе, отмеченном [Then]?

4b9b3361

Ответ 1

Отличный вопрос. Я не эксперт по bdd или specflow, однако мой первый совет - сделать шаг назад и оценить ваш сценарий.

Вы действительно хотите использовать термины "throw" и "exception" в этой спецификации? Имейте в виду, что идея с bdd - использовать вездесущий язык для бизнеса. В идеале они должны уметь читать эти сценарии и интерпретировать их.

Подумайте об изменении своей фразы "then", чтобы включить что-то вроде этого:

Scenario: Pressing add with an empty stack displays an error
    Given I have entered nothing into the calculator
    When I press add
    Then the user is presented with an error message

Исключение все еще бросается в фоновом режиме, но конечным результатом является простое сообщение об ошибке.

Скотт Беллоуш затрагивает эту концепцию в этом подкасте "Колебания": http://herdingcode.com/?p=176

Ответ 2

Как новичок в SpecFlow, я не буду говорить вам, что это способ сделать это, но одним из способов сделать это было бы использовать ScenarioContext для хранения исключения, созданного в Когда;

try
{
    calculator.Add(1,1);
}
catch (Exception e)
{
    ScenarioContext.Current.Add("Exception_CalculatorAdd", e);
}

В Затем вы можете проверить возникшее исключение и сделать на нем подтверждения;

var exception = ScenarioContext.Current["Exception_CalculatorAdd"];
Assert.That(exception, Is.Not.Null);

Сказанное; Я согласен с scoarescoare, когда он говорит, что вы должны сформулировать сценарий в немного более "удобных для бизнеса" формулировках. Тем не менее, использование SpecFlow для управления реализацией вашей модели домена, может быть полезно при использовании исключений и выполнения утверждений.

Btw: Отъезд Rob Conery screencast на TekPub для некоторых действительно хороших советов по использованию SpecFlow: http://tekpub.com/view/concepts/5

Ответ 3

BDD можно практиковать на уровне уровня поведения или/и на уровне уровня.

SpecFlow - это инструмент BDD, который фокусируется на поведении на уровне объектов. Исключения - это не то, что вы должны указать/наблюдать за поведением на уровне объектов. Исключения должны указываться/наблюдаться при поведении на уровне единицы.

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

Если у вас нет каких-либо нетехнических заинтересованных сторон, то SpecFlow - неправильный инструмент для вас! Не тратьте энергию на создание бизнес-удобочитаемых спецификаций, если их никто не интересует.

Существуют инструменты BDD, которые фокусируются на поведении на уровне единицы. В .NET наиболее популярным является MSpec (http://github.com/machine/machine.specifications). BDD на уровне единицы также может быть легко практикой со стандартными модульными модулями тестирования.

Тем не менее, вы все еще можете проверить исключение в SpecFlow.

Вот еще несколько обсуждений bdd на уровне единицы vs. bdd на уровне функции: SpecFlow/BDD vs Unit Testing BDD для тестов приемочного тестирования против BDD для модульных тестов (или: ATDD против TDD)

Также посмотрите этот пост в блоге: Классификация BDD-инструментов (управляемый модулем и приемочным тестированием) и немного истории BDD

Ответ 4

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

  • Поймайте исключение (я действительно рекомендую улавливать определенные исключения, если вам действительно не нужно поймать все) на этапе, который вызывает операцию и передает ее в контекст сценария.

    [When("I press add")]
    public void WhenIPressAdd()
    {
       try
       {
         _calc.Add();
       }
       catch (Exception err)
       {
          ScenarioContext.Current[("Error")] = err;
       }
    }
    
  • Подтвердить, что это исключение хранится в контексте сценария

    [Then(@"it should throw an exception")]
    public void ThenItShouldThrowAnException()
    {
          Assert.IsTrue(ScenarioContext.Current.ContainsKey("Error"));
    }
    

P.S. Это очень близко к одному из существующих ответов. Однако, если вы попытаетесь получить значение из ScenarioContext, используя синтаксис, как показано ниже:

var err = ScenarioContext.Current["Error"]

он выдает другое исключение в случае, если ключ "Ошибка" не существует (и это приведет к сбою всех сценариев, которые выполняют вычисления с правильными параметрами). Поэтому ScenarioContext.Current.ContainsKey может быть более подходящим

Ответ 5

В случае, если вы тестируете взаимодействие с пользователем, я буду только советовать, что уже было сказано о том, чтобы сосредоточиться на пользовательском опыте: "Тогда пользователю будет предоставлено сообщение об ошибке". Но, если вы тестируете уровень ниже пользовательского интерфейса, я хотел бы поделиться своим опытом:

Я использую SpecFlow для разработки бизнес-уровня. В моем случае я не забочусь о взаимодействиях пользовательского интерфейса, но я по-прежнему считаю чрезвычайно полезным подход BDD и SpecFlow.

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

В настоящее время я использую явные предложения "Then", несколько раз без "Когда", таким образом:

Scenario: Adding with an empty stack causes an error
     Given I have entered nothing into the calculator
     Then adding causes an error X

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

Ответ 6

Мое решение включает в себя несколько элементов для реализации, но в самом конце это будет выглядеть намного элегантнее:

@CatchException
Scenario: Faulty operation throws exception
    Given Some Context
    When Some faulty operation invoked
    Then Exception thrown with type 'ValidationException' and message 'Validation failed'

Чтобы выполнить эту работу, выполните следующие три шага:

Шаг 1

Отметьте сценарии, в которых вы ожидаете исключений с помощью некоторого тега, например. @CatchException:

@CatchException
Scenario: ...

Шаг 2

Определите обработчик AfterStep, чтобы изменить ScenarioContext.TestStatus на OK. Вам может потребоваться только игнорировать ошибки для шагов "Когда", так что вы все равно можете пропустить тест в Then, проверяя исключение. Должно было сделать это через отражение, поскольку свойство TestStatus является внутренним:

[AfterStep("CatchException")]
public void CatchException()
{
    if (ScenarioContext.Current.StepContext.StepInfo.StepDefinitionType == StepDefinitionType.When)
    {
        PropertyInfo testStatusProperty = typeof(ScenarioContext).GetProperty("TestStatus", BindingFlags.NonPublic | BindingFlags.Instance);
        testStatusProperty.SetValue(ScenarioContext.Current, TestStatus.OK);
    }
}

Шаг 3

Подтвердите TestError так же, как вы бы подтвердили что-либо в ScenarioContext.

[Then(@"Exception thrown with type '(.*)' and message '(.*)'")]
public void ThenExceptionThrown(string type, string message)
{
    Assert.AreEqual(type, ScenarioContext.Current.TestError.GetType().Name);
    Assert.AreEqual(message, ScenarioContext.Current.TestError.Message);
}