Как получить SpecFlow ожидать исключения? - PullRequest
40 голосов
/ 21 мая 2010

Я использую 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

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

Ответы [ 6 ]

39 голосов
/ 22 мая 2010

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

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

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

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

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

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

37 голосов
/ 23 мая 2010

Как новичок в 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 для управления реализацией вашей доменной модели может помочь в обнаружении исключений и выполнении утверждений на них.

Кстати: посмотрите скринкаст Роба Конери на TekPub, где вы найдете несколько полезных советов по использованию SpecFlow: http://tekpub.com/view/concepts/5

14 голосов
/ 31 мая 2010

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

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

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

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

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

Тем не менее, вы могли бы по-прежнему проверять исключение в SpecFlow .

Вот еще несколько обсуждений bdd на уровне устройства и bdd на уровне объектов: SpecFlow / BDD против модульного тестирования BDD для приемочных испытаний против BDD для юнит-тестов (или: ATDD против TDD)

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

7 голосов
/ 07 июля 2010

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

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

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

    [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 голосов
/ 15 февраля 2017

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

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

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

Шаг 1

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

@CatchException
Scenario: ...

Шаг 2

Определите обработчик AfterStep, чтобы изменить ScenarioContext.TestStatus на OK. Возможно, вы захотите игнорировать ошибки только для при шагах, поэтому вы все равно можете не пройти тест в Затем , проверяющем исключение. Пришлось сделать это с помощью отражения, так как 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);
}
5 голосов
/ 14 июня 2011

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

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

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

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

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

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

...