utilizar try todas tipos que para net manejo manejar las existen excepciones errores como catch capturar asp c# unit-testing mstest assert vs-unit-testing-framework

c# - try - ¿Cómo uso Assert para verificar que se ha lanzado una excepción?



todas las excepciones en c# (21)

¿Esto va a depender de qué marco de prueba está utilizando?

En MbUnit, por ejemplo, puede especificar la excepción esperada con un atributo para asegurarse de que está obteniendo la excepción que realmente espera.

[ExpectedException(typeof(ArgumentException))]

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


Aunque esta es una pregunta antigua, me gustaría agregar un nuevo pensamiento a la discusión. He extendido el patrón Organizar, Actuar, Asertar para ser esperado, Organizar, Actuar, Afirmar. Puede hacer un puntero de excepción esperado y, a continuación, afirmar que se asignó a. Esto se siente más limpio que hacer tus Asserts en un bloque catch, dejando a tu sección Act en su mayoría solo para que la línea de código llame al método bajo prueba. Tampoco tienes que Assert.Fail(); o return desde múltiples puntos en el código. Cualquier otra excepción lanzada causará que la prueba falle, porque no se detectará, y si se lanza una excepción del tipo esperado, pero no fue la que esperaba, afirmando contra el mensaje u otras propiedades de la excepción ayuda a asegurar que su prueba no pase inadvertidamente.

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


Bueno, resumiré bastante bien lo que todos los demás aquí dijeron antes ... De todos modos, aquí está el código que construí de acuerdo con las buenas respuestas :) Todo lo que queda por hacer es copiar y usar ...

/// <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."); }


Como alternativa, puede probar las excepciones de prueba que de hecho se lanzan con las siguientes 2 líneas en su prueba.

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


Echa un vistazo a nUnit Docs para ver ejemplos sobre:

[ExpectedException( typeof( ArgumentException ) )]


El ayudante proporcionado por @Richiban anteriormente funciona muy bien, excepto que no maneja 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)) ); } } } }


En caso de usar NUnit , intente esto:

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


En las pruebas unitarias integradas de VS, si simplemente desea verificar que se produce "cualquier excepción", pero no conoce el tipo, puede usar un catch all:

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


En un proyecto en el que estoy trabajando tenemos otra solución para hacer esto.

En primer lugar, no me gusta el atributo de excepción de expectativa, ya que toma en consideración el método que provocó la excepción.

Hago esto con un método de ayuda en su 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()); }

Método de ayuda

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

Aseado, no es eso;)


Es un atributo en el método de prueba ... no usas Assert. Se ve como esto:

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


MSTest ahora tiene una función Assert.ThrowsException que se puede usar así:

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


Mi método preferido para implementar esto es escribir un método llamado Tiros, y usarlo como cualquier otro método Assert. Desafortunadamente, .NET no le permite escribir un método de extensión estática, por lo que no puede usar este método como si realmente perteneciera a la compilación en la clase Assert; Solo haz otro llamado MyAssert o algo similar. La clase se ve así:

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

Eso significa que tu prueba de unidad se ve así:

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

Que se ve y se comporta mucho más como el resto de sus sintaxis de prueba de unidad.


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). Use un método de afirmación bien diseñado, ya sea proporcionado por su marco de prueba o escriba el suyo. 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)); } }

Ejemplos de uso:

[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 decir tiros de imagen: tirados y no atrapados.

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 en qué técnica utilizar para cada prueba, sino que cambiar el código de una técnica a otra a medida que cambian las necesidades no fue un esfuerzo trivial. El uso de un enfoque consistente ahorra esfuerzo mental.

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


Para "Visual Studio Team Test" parece que aplica el atributo ExpectedException al método de la prueba.

Ejemplo de la documentación aquí: Un tutorial de pruebas de unidad con Visual Studio Team Test

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


Por lo general, su marco de prueba tendrá una respuesta para esto. Pero si no es lo suficientemente flexible, siempre puedes 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 capturar una excepción base:

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

Si absolutamente debe capturar Excepción, debe volver a lanzar Assert.Fail (). Pero realmente, esta es una señal de que no deberías estar escribiendo esto a mano; revise su marco de prueba para ver las opciones, o vea si puede lanzar una excepción más significativa para probar.

catch (AssertionException) { throw; }

Debería poder adaptar este enfoque a lo que quiera, incluida la especificación de los tipos de excepciones a capturar. Si solo esperas ciertos tipos, termina los bloques de catch con:

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


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

Instrucciones de alto nivel: descargue el ensamblaje y herede de BaseTest y puede usar la sintaxis Assert.Throws () .

El método principal para la implementación de Throws 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: Junté este paquete.

Más información: http://www.bradoncode.com/blog/2012/01/asserting-exceptions-in-mstest-with.html


Puedes lograr esto con una simple línea.

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

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

Si foo.bar() no es async

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


Si está utilizando MSTest, que originalmente no tenía un atributo ExpectedException , podría hacer esto:

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


Si usas NUNIT, puedes hacer algo como esto:

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


También es posible almacenar la excepción lanzada para validarla más:

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

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


Tenga cuidado con el uso de ExpectedException, ya que puede llevar a varios escollos, como se demuestra aquí:

http://geekswithblogs.net/sdorman/archive/2009/01/17/unit-testing-and-expected-exceptions.aspx

Y aquí:

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

Si necesita probar las excepciones, hay menos maneras mal vistas. Puede usar el método try {act / fail} catch {assert}, que puede ser útil para los marcos que no tienen soporte directo para pruebas de excepción que no sean ExpectedException.

Una mejor alternativa es usar xUnit.NET, que es un marco de prueba de unidades extensible muy moderno, con visión de futuro y extensible que ha aprendido de todos los demás errores y mejorado. Una mejora de este tipo es Assert.Throws, que proporciona una sintaxis mucho mejor para establecer excepciones.

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


Ya que 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); });

Digamos que tenemos el requisito de que el cliente debe tener una dirección para crear un pedido . Si no, el método CreateOrderForCustomer debería dar como resultado una ArgumentException . Entonces podríamos 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íficos acerca de lo que debería generar el error. Esto hace que los requisitos en nuestras pruebas sean más claros y también facilita el diagnóstico cuando la prueba falla.

Tenga en cuenta que también hay un Should.ThrowAsync para la prueba de métodos asíncronos.