¿Cómo puedo hacer que SpecFlow espere una excepción?


Estoy usando SpecFlow, y me gustaría escribir un escenario como el siguiente:

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

Es calculator.Add() que va a lanzar una excepción, así que ¿cómo manejo esto en el método marcado [Then]?

Author: Roger Lipscombe, 2010-05-21

6 answers

Gran pregunta. No soy un experto en bdd o specflow, sin embargo, mi primer consejo sería dar un paso atrás y evaluar su escenario.

¿Realmente quieres usar los términos "lanzar" y "excepción" en esta especificación? Tenga en cuenta que la idea con bdd es usar un lenguaje ubicuo con el negocio. Idealmente, deberían ser capaces de leer estos escenarios e interpretarlos.

Considere cambiar su frase "entonces" para incluir algo como esto:

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

El la excepción sigue apareciendo en segundo plano, pero el resultado final es un simple mensaje de error.

Scott Bellware toca este concepto en este podcast de Herding Code: http://herdingcode.com/?p=176

 39
Author: Scott Coates,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-08 14:56:04

Como novato en SpecFlow no te diré que esta es la forma de hacerlo, pero una forma de hacerlo sería usar el ScenarioContext para almacenar la excepción lanzada en el Cuando ;

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

En su Entonces usted podría comprobar la excepción lanzada y hacer afirmaciones sobre ella;

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

Dicho esto, estoy de acuerdo con scoarescoare cuando dice que debe formular el escenario en un texto un poco más "amigable para los negocios". Sin embargo, el uso de SpecFlow para conducir el la implementación de su modelo de dominio, la captura de excepciones y hacer afirmaciones sobre ellos puede ser útil.

Por cierto: Echa un vistazo al screencast de Rob Conery en TekPub para algunos consejos realmente buenos sobre el uso de SpecFlow: http://tekpub.com/view/concepts/5

 36
Author: Kjetil Klaussen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-15 17:43:31

El BDD se puede practicar en el comportamiento a nivel de entidad o/y en el comportamiento a nivel de unidad.

SpecFlow es una herramienta BDD que se centra en el comportamiento a nivel de entidad. Las excepciones no son algo que deba especificar/observar en el comportamiento a nivel de entidad. Se deben especificar/observar excepciones en el comportamiento a nivel de unidad.

Piense en los escenarios de SpecFlow como una especificación en vivo para el stakeholder no técnico. Tampoco escribiría en la especificación que se lanza una excepción, pero cómo el sistema se comporta en tal caso.

Si usted no tiene ninguna parte interesada no técnica, entonces SpecFlow es la herramienta equivocada para usted! No desperdicie energía en la creación de especificaciones legibles para negocios si no hay nadie interesado en leerlas.

Hay herramientas BDD que se centran en el comportamiento a nivel de unidad. En. NET el más popular es MSpec ( http://github.com/machine/machine.specifications). BDD a nivel de unidad también puede ser fácilmente prácticas con pruebas unitarias estándar marco.

Dicho esto, todavía podría comprobar si hay una excepción en SpecFlow.

Aquí hay un poco más de discusión de bdd a nivel de unidad vs. bdd a nivel de característica: SpecFlow / BDD vs Unit Testing BDD para Pruebas de Aceptación vs. BDD para Pruebas Unitarias (o: ATDD vs. TDD)

También echa un vistazo a esta entrada del blog: Clasificar las herramientas de BDD (Impulsadas por Pruebas Unitarias vs. Impulsadas por Pruebas de Aceptación) y un poco de historia de BDD

 13
Author: jbandi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-31 13:25:54

Cambiar el escenario para no tener una excepción es probablemente una buena manera de tener el escenario más orientado al usuario. Sin embargo, si aún necesita que funcione, considere lo siguiente:

  1. Captura una excepción (realmente recomiendo capturar excepciones específicas a menos que realmente necesite capturar todas) en el paso que invoca una operación y la pasa al contexto del escenario.

    [When("I press add")]
    public void WhenIPressAdd()
    {
       try
       {
         _calc.Add();
       }
       catch (Exception err)
       {
          ScenarioContext.Current[("Error")] = err;
       }
    }
    
  2. Validar que la excepción se almacena en el escenario context

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

P.d. Está muy cerca de una de las respuestas existentes. Sin embargo, si intenta obtener valor de ScenarioContext usando sintaxis como la siguiente:

var err = ScenarioContext.Current["Error"]

Lanzará otra excepción en caso de que la clave "Error" no exista (y eso fallará en todos los escenarios que realicen cálculos con parámetros correctos). Así que ScenarioContext.Current.ContainsKey puede ser más apropiado

 7
Author: Dmitriy Chernyavsky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-17 15:42:43

En caso de que esté probando las interacciones del usuario, solo aconsejaré lo que ya se ha dicho sobre centrarse en la experiencia del usuario: "Entonces se le presenta al usuario un mensaje de error". Pero, en caso de que esté probando un nivel por debajo de la interfaz de usuario, me gustaría compartir mi experiencia:

Estoy usando SpecFlow para desarrollar una capa de negocio. En mi caso, no me importan las interacciones de la interfaz de usuario, pero todavía encuentro extremadamente útil el enfoque BDD y SpecFlow.

En la capa de negocio no quiero especificaciones que decir "Entonces el usuario se presenta con un mensaje de error", pero en realidad la verificación de que el servicio responde correctamente a una entrada incorrecta. He hecho por un tiempo lo que ya se ha dicho de atrapar la excepción en el " Cuándo "y verificarla en el" Entonces", pero encuentro que esta opción no es óptima, porque si reutilizas el paso" Cuándo " podrías tragar una excepción donde no lo esperabas.

Actualmente, estoy usando cláusulas explícitas de "Entonces", algunas veces sin el "Cuándo", esto camino:

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

Esto me permite codificar específicamente la acción y la detección de excepciones en un solo paso. Puedo reutilizarlo para probar tantos casos de error como quiera y no me hace agregar código no relacionado a los pasos "Cuando" que no fallan.

 4
Author: Francesc Castells,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-14 00:20:52

Mi solución implica un par de elementos para implementar, pero al final se verá mucho más elegante:

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

Para hacer que esto funcione, siga esos 3 pasos:

Paso 1

Marque los escenarios en los que espera excepciones con alguna etiqueta, por ejemplo @CatchException:

@CatchException
Scenario: ...

Paso 2

Define un manejador AfterStep para cambiar ScenarioContext.TestStatus a OK. Es posible que solo desee ignorar errores en para Cuando pasos, por lo que todavía puede fallar una prueba en Luego verificando una excepción. Tuvo que hacer esto a través de la reflexión, ya que TestStatus la propiedad es interna:

[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);
    }
}

Paso 3

Valida TestError de la misma manera que validaría cualquier cosa dentro de 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);
}
 4
Author: Vitaliy Ulantikov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-05 20:07:57