Pregunta

¿Cómo uso Assert (u otra clase de prueba?) para verificar que se haya lanzado una excepción?

¿Fue útil?

Solución

En "Prueba Visual Studio Team" Parece que aplica el atributo ExpectedException el método de la prueba.

Muestra de la documentación aquí: Una Unidad de Pruebas Tutorial con Visual Studio Test Team

[TestMethod]
[ExpectedException(typeof(ArgumentException),
    "A userId of null was inappropriately allowed.")]
public void NullUserIdInConstructor()
{
   LogonInfo logonInfo = new LogonInfo(null, "P@ss0word");
}

Otros consejos

Por lo general su marco de pruebas tendrá una respuesta para esto. Pero si no es lo suficientemente flexible, siempre se puede hacer esto:

try {
    somethingThatShouldThrowAnException();
    Assert.Fail(); // If it gets to this line, no exception was thrown
} catch (GoodException) { }

Como @Jonas señala, esto no funciona para la captura de una base de Excepción:

try {
    somethingThatShouldThrowAnException();
    Assert.Fail(); // raises AssertionException
} catch (Exception) {
    // Catches the assertion exception, and the test passes
}

Si es absolutamente necesario ponerse de excepción, es necesario volver a lanzar el Assert.Fail (). Pero en realidad, esto es una señal que no debería ser esto-escritura a mano; revisar su marco de pruebas para las opciones, o ver si se puede lanzar una excepción más significativa para probar.

catch (AssertionException) { throw; }

Usted debe ser capaz de adaptar este enfoque a lo que quiera - incluida la especificación de los tipos de excepciones para la captura. Si sólo se espera cierto tipo, termine los bloques catch con:

} catch (GoodException) {
} catch (Exception) {
    // not the right kind of exception
    Assert.Fail();
}

Mi método preferido para implementar esto es escribir un método llamado Lanza, y utilizarlo como cualquier otro método Assert. Por desgracia, .NET no permite que usted pueda escribir un método de extensión estática, por lo que no se puede utilizar este método como si en realidad pertenece a la acumulación en la clase Assert; acaba de hacer otra llamada MyAssert o algo similar. La clase tiene el siguiente aspecto:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace YourProject.Tests
{
    public static class MyAssert
    {
        public static void Throws<T>( Action func ) where T : Exception
        {
            var exceptionThrown = false;
            try
            {
                func.Invoke();
            }
            catch ( T )
            {
                exceptionThrown = true;
            }

            if ( !exceptionThrown )
            {
                throw new AssertFailedException(
                    String.Format("An exception of type {0} was expected, but not thrown", typeof(T))
                    );
            }
        }
    }
}

Esto significa que su unidad de prueba es el siguiente:

[TestMethod()]
public void ExceptionTest()
{
    String testStr = null;
    MyAssert.Throws<NullReferenceException>(() => testStr.ToUpper());
}

Lo que se ve y se comporta mucho más como el resto de sus sintaxis de las pruebas unitarias.

Si está utilizando MSTest, que originalmente no tiene un atributo ExpectedException, usted puede hacer esto:

try 
{
    SomeExceptionThrowingMethod()
    Assert.Fail("no exception thrown");
}
catch (Exception ex)
{
    Assert.IsTrue(ex is SpecificExceptionType);
}

si utiliza nUnit, se puede hacer algo como esto:

Assert.Throws<ExpectedException>(() => methodToTest());


También es posible almacenar la excepción lanzada con el fin de validar aún más:

ExpectedException ex = Assert.Throws<ExpectedException>(() => methodToTest());
Assert.AreEqual( "Expected message text.", ex.Message );
Assert.AreEqual( 5, ex.SomeNumber);

Ver: http://nunit.org/docs/2.5/exceptionAsserts.html

Tenga cuidado con el uso de ExpectedException, ya que puede conducir a varios escollos como se ha demostrado aquí:

http: // geekswithblogs. net / sdorman / archivo / 2009/01/17 / unidad-prueba-y-espera-exceptions.aspx

Y aquí:

http://xunit.github.io/docs/comparisons.html

Si usted necesita para poner a prueba para las excepciones, hay menos mal visto maneras. Se puede utilizar el método de prueba {act / fallar} catch {} afirman, que puede ser útil para los marcos que no tienen el apoyo directo para las pruebas de excepción que no sean ExpectedException.

Una mejor alternativa es utilizar xUnit.NET, que es un muy moderno hacia el futuro, y el marco de las pruebas, la unidad extensible que ha aprendido de los errores de otros, y mejorado. Una de estas mejoras es Assert.Throws, que proporciona una mejor sintaxis para hacer valer excepciones.

Puede encontrar xUnit.NET en github: http://xunit.github.io/

En un proyecto estoy trabajando en otra solución que tenemos haciendo esto.

En primer lugar no me gusta la ExpectedExceptionAttribute Becuase lo hace tomar en consideración qué llamada método que provocó la excepción.

Lo hago con un helpermethod lugar.

Prueba

[TestMethod]
public void AccountRepository_ThrowsExceptionIfFileisCorrupt()
{
     var file = File.Create("Accounts.bin");
     file.WriteByte(1);
     file.Close();

     IAccountRepository repo = new FileAccountRepository();
     TestHelpers.AssertThrows<SerializationException>(()=>repo.GetAll());            
}

HelperMethod

public static TException AssertThrows<TException>(Action action) where TException : Exception
    {
        try
        {
            action();
        }
        catch (TException ex)
        {
            return ex;
        }
        Assert.Fail("Expected exception was not thrown");

        return null;
    }

Neat, ¿o no;)

MSTest (v2) tiene ahora una función Assert.ThrowsException que puede ser utilizado como esto:

Assert.ThrowsException<System.FormatException>(() =>
            {
                Story actual = PersonalSite.Services.Content.ExtractHeader(String.Empty);
            }); 

Se puede instalar con Nuget: Install-Package MSTest.TestFramework

Es un atributo del método de ensayo ... no utiliza Assert. Se ve así:

[ExpectedException(typeof(ExceptionType))]
public void YourMethod_should_throw_exception()

Puede descargar un paquete de Nuget usando: PM> Install-Package MSTestExtensions que se suma Assert.Throws () de sintaxis en el estilo de nUnit / xUnit a MSTEST.

instrucciones de alto nivel:. Descargar el montaje y heredar de BaseTest y puede utilizar los Assert.Throws () sintaxis

El método principal de la lanza aplicación es el siguiente:

public static void Throws<T>(Action task, string expectedMessage, ExceptionMessageCompareOptions options) where T : Exception
{
    try
    {
        task();
    }
    catch (Exception ex)
    {
        AssertExceptionType<T>(ex);
        AssertExceptionMessage(ex, expectedMessage, options);
        return;
    }

    if (typeof(T).Equals(new Exception().GetType()))
    {
        Assert.Fail("Expected exception but no exception was thrown.");
    }
    else
    {
        Assert.Fail(string.Format("Expected exception of type {0} but no exception was thrown.", typeof(T)));
    }
}

Divulgación: he creado este paquete

.

Más información: http: // www .bradoncode.com / blog / 2012/01 / afirmando-excepciones-en-mstest-with.html

Esto se puede conseguir con una simple línea.

Si su foo.bar() operación es asíncrona:

await Assert.ThrowsExceptionAsync<Exception>(() => foo.bar());

Si no es foo.bar() asíncrono

Assert.ThrowsException<Exception>(() => foo.bar());

No recomiendo usar el atributo ExpectedException (ya que es demasiado restrictivo y propenso a errores) o escribir un bloque try/catch en cada prueba (ya que es demasiado complicado y propenso a errores).Utilice un método de afirmación bien diseñado, ya sea proporcionado por su marco de prueba o escriba el suyo propio.Esto es lo que escribí y uso.

public static class ExceptionAssert
{
    private static T GetException<T>(Action action, string message="") where T : Exception
    {
        try
        {
            action();
        }
        catch (T exception)
        {
            return exception;
        }
        throw new AssertFailedException("Expected exception " + typeof(T).FullName + ", but none was propagated.  " + message);
    }

    public static void Propagates<T>(Action action) where T : Exception
    {
        Propagates<T>(action, "");
    }

    public static void Propagates<T>(Action action, string message) where T : Exception
    {
        GetException<T>(action, message);
    }

    public static void Propagates<T>(Action action, Action<T> validation) where T : Exception
    {
        Propagates(action, validation, "");
    }

    public static void Propagates<T>(Action action, Action<T> validation, string message) where T : Exception
    {
        validation(GetException<T>(action, message));
    }
}

Usos de ejemplo:

    [TestMethod]
    public void Run_PropagatesWin32Exception_ForInvalidExeFile()
    {
        (test setup that might propagate Win32Exception)
        ExceptionAssert.Propagates<Win32Exception>(
            () => CommandExecutionUtil.Run(Assembly.GetExecutingAssembly().Location, new string[0]));
        (more asserts or something)
    }

    [TestMethod]
    public void Run_PropagatesFileNotFoundException_ForExecutableNotFound()
    {
        (test setup that might propagate FileNotFoundException)
        ExceptionAssert.Propagates<FileNotFoundException>(
            () => CommandExecutionUtil.Run("NotThere.exe", new string[0]),
            e => StringAssert.Contains(e.Message, "NotThere.exe"));
        (more asserts or something)
    }

NOTAS

Devolver la excepción en lugar de admitir una devolución de llamada de validación es una idea razonable, excepto que hacerlo hace que la sintaxis de llamada de esta afirmación sea muy diferente a otras afirmaciones que uso.

A diferencia de otros, uso 'propagaciones' en lugar de 'lanzamientos' ya que solo podemos probar si una excepción se propaga desde una llamada.No podemos probar directamente que se lanza una excepción.Pero supongo que podrías imaginar que los lanzamientos significan:arrojado y no atrapado.

PENSAMIENTO FINAL

Antes de cambiar a este tipo de enfoque, consideré usar el atributo ExpectedException cuando una prueba solo verificaba el tipo de excepción y usar un bloque try/catch si se requería más validación.Pero no solo tendría que pensar qué técnica usar para cada prueba, sino que cambiar el código de una técnica a otra a medida que cambiaban las necesidades no era un esfuerzo trivial.Usar un enfoque consistente ahorra esfuerzo mental.

Entonces, en resumen, este enfoque deportivo:facilidad de uso, flexibilidad y robustez (es difícil hacerlo mal).

El ayudante proporcionada por @Richiban anterior funciona muy bien excepto que no se ocupa de la situación en la que se produce una excepción, pero no el tipo esperado. Las siguientes direcciones que:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace YourProject.Tests
{
    public static class MyAssert
    {
        /// <summary>
        /// Helper for Asserting that a function throws an exception of a particular type.
        /// </summary>
        public static void Throws<T>( Action func ) where T : Exception
        {
            Exception exceptionOther = null;
            var exceptionThrown = false;
            try
            {
                func.Invoke();
            }
            catch ( T )
            {
                exceptionThrown = true;
            }
            catch (Exception e) {
                exceptionOther = e;
            }

            if ( !exceptionThrown )
            {
                if (exceptionOther != null) {
                    throw new AssertFailedException(
                        String.Format("An exception of type {0} was expected, but not thrown. Instead, an exception of type {1} was thrown.", typeof(T), exceptionOther.GetType()),
                        exceptionOther
                        );
                }

                throw new AssertFailedException(
                    String.Format("An exception of type {0} was expected, but no exception was thrown.", typeof(T))
                    );
            }
        }
    }
}

Desde que usted menciona el uso de otras clases de prueba, una mejor opción que el atributo ExpectedException es usar Shoudly ' s Should.Throw .

Should.Throw<DivideByZeroException>(() => { MyDivideMethod(1, 0); });

Supongamos que tenemos un requisito de que el cliente debe tener un Dirección para crear un Para . Si no es así, el método CreateOrderForCustomer debería resultar en un ArgumentException. Entonces podemos escribir:

[TestMethod]
public void NullUserIdInConstructor()
{
  var customer = new Customer(name := "Justin", address := null};

  Should.Throw<ArgumentException>(() => {
    var order = CreateOrderForCustomer(customer) });
}

Esto es mejor que usar un atributo ExpectedException porque estamos siendo específico acerca de lo que debería tirar el error. Esto hace que los requisitos en nuestras pruebas más claras y también hace más fácil el diagnóstico cuando la prueba falla.

Nota también hay un Should.ThrowAsync para las pruebas método asincrónico.

Como alternativa, se puede intentar probar las excepciones son, de hecho, ser arrojado con los siguientes 2 líneas en su prueba.

var testDelegate = () => MyService.Method(params);
Assert.Throws<Exception>(testDelegate);

Bueno voy a prácticamente resumir lo que todos los demás aquí dijo antes ... De todas formas, aquí está el código que construye de acuerdo a las buenas respuestas :) Todo queda por hacer es copiar y uso ...

/// <summary>
/// Checks to make sure that the input delegate throws a exception of type TException.
/// </summary>
/// <typeparam name="TException">The type of exception expected.</typeparam>
/// <param name="methodToExecute">The method to execute to generate the exception.</param>
public static void AssertRaises<TException>(Action methodToExecute) where TException : System.Exception
{
    try
    {
        methodToExecute();
    }
    catch (TException) {
        return;
    }  
    catch (System.Exception ex)
    {
        Assert.Fail("Expected exception of type " + typeof(TException) + " but type of " + ex.GetType() + " was thrown instead.");
    }
    Assert.Fail("Expected exception of type " + typeof(TException) + " but no exception was thrown.");  
}

En VS incorporado en la unidad de pruebas si simplemente desea comprobar que "ninguna excepción" es lanzada, pero usted no sabe el tipo, puede utilizar una captura todo:

[TestMethod]
[ExpectedException(typeof(Exception), AllowDerivedTypes = true)]
public void ThrowExceptionTest()
{
    //...
}

Verificar nDocumentos de la unidad para ejemplos sobre:

[ExpectedException( typeof( ArgumentException ) )]

Esto va a depender de qué marco de pruebas está usando?

En MbUnit, por ejemplo, puede especificar la excepción de esperar con un atributo para asegurarse de que está recibiendo la excepción que realmente esperas.

[ExpectedException(typeof(ArgumentException))]

En el caso de utilizar NUnit , intente esto:

Assert.That(() =>
        {
            Your_Method_To_Test();
        }, Throws.TypeOf<Your_Specific_Exception>().With.Message.EqualTo("Your_Specific_Message"));

A pesar de que esta es una vieja pregunta, me gustaría añadir un nuevo pensamiento a la discusión. He ampliado la amueblar, Ley, afirmamos patrón de esperarse, los arreglos, Ley, afirman. Puede hacer que un puntero de excepción esperada, entonces afirmar que fue asignado. Esto se siente más limpio que hace su Afirma en un bloque catch, dejando la sección de Ley sobre todo sólo por una línea de código para llamar al método que se está probando. También no tiene que Assert.Fail(); o return desde múltiples puntos del código. Cualquier otra excepción que se va a hacer que la prueba falle, porque no va a ser capturado, y si se produce una excepción de su tipo esperado, pero el no fue el que esperaba, afirmando en contra del mensaje u otras propiedades de la excepción ayudar a asegurarse de que su prueba no pasará inadvertida.

[TestMethod]
public void Bar_InvalidDependency_ThrowsInvalidOperationException()
{
    // Expectations
    InvalidOperationException expectedException = null;
    string expectedExceptionMessage = "Bar did something invalid.";

    // Arrange
    IDependency dependency = DependencyMocks.Create();
    Foo foo = new Foo(dependency);

    // Act
    try
    {
        foo.Bar();
    }
    catch (InvalidOperationException ex)
    {
        expectedException = ex;
    }

    // Assert
    Assert.IsNotNull(expectedException);
    Assert.AreEqual(expectedExceptionMessage, expectedException.Message);
}

Hay una biblioteca impresionante llamada NFluent que acelera y facilita la forma en que escribir sus afirmaciones .

Es bastante sencillo para escribir una afirmación para lanzar una excepción:

    [Test]
    public void given_when_then()
    {
        Check.ThatCode(() => MethodToTest())
            .Throws<Exception>()
            .WithMessage("Process has been failed");
    }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top