[TestMethod][ExpectedException(typeof(ArgumentException),"A userId of null was inappropriately allowed.")]publicvoidNullUserIdInConstructor(){LogonInfo logonInfo =newLogonInfo(null,"P@ss0word");}
El atributo ExpectedException anterior también funciona en NUnit (pero [TestMethod] debería ser [Test]).
dbkk 01 de
55
@dbkk: no funciona exactamente igual en NUnit: el mensaje se trata como una cadena que debe coincidir con el mensaje de excepción (y creo que tiene más sentido)
Ruben Bartelink el
29
Este atributo hace el trabajo y es una característica incorporada para los programadores de C #, pero no recomiendo usarlo ya que no es lo suficientemente flexible. Considere qué sucede si el código de configuración de la prueba arroja el tipo de excepción: la prueba pasa, pero en realidad no hizo lo que esperaba. O qué pasa si desea probar el estado del objeto de excepción. Por lo general, quiero usar StringAssert.Contains (e.Message ...) en lugar de probar todo el mensaje. Use un método de afirmación como se describe en otras respuestas.
Steve
3
Evite usar ExpectedException en NUnit, ya que se eliminará en NUnit 3.0. Prefiero usar Assert.Throws <SpecificException> ()
Terence
55
Puede usar Assert.ThrowsException <T> y Assert.ThrowsExceptionAsync <T> dentro de MsTest.
Gopal Krishnan
257
Por lo general, su marco de prueba tendrá una respuesta para esto. Pero si no es lo suficientemente flexible, siempre 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 atrapar una excepción base:
try{
somethingThatShouldThrowAnException();Assert.Fail();// raises AssertionException}catch(Exception){// Catches the assertion exception, and the test passes}
Si absolutamente debe atrapar Exception, debe volver a lanzar Assert.Fail (). Pero realmente, esta es una señal de que no deberías escribir esto a mano; verifique 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 qué tipos de excepciones se deben capturar. Si solo espera ciertos tipos, termine los catchbloques con:
}catch(GoodException){}catch(Exception){// not the right kind of exceptionAssert.Fail();}
+1, lo uso de esta manera en lugar del atributo cuando necesito hacer afirmaciones más allá del tipo de excepción. Por ejemplo, ¿qué pasa si uno necesita verificar que ciertos campos en la instancia de excepción estén configurados con ciertos valores?
Pavel Repin
2
No es necesario que especifique el mensaje de error. Esto es suficiente: [ExpectedException (typeof (ArgumentException))]
mibollma
55
Creo que esta solución es la mejor. [ExpectedException (typeof (ArgumentException))] tiene sus usos, si la prueba es simple, pero desde mi punto de vista es una solución perezosa y estar cómodo puede llevar a dificultades. Esta solución le brinda un control específico para hacer una prueba más correcta, además de que puede realizar una prueba de Writeline en su informe de ejecución de prueba, de que la excepción se arrojó como se esperaba.
evilfish
12
Tenga cuidado con eso porque Assert.Fail () genera una excepción, si lo atrapa, ¡la prueba pasa!
Jonas
44
@ Vinnyq12 Lo que quiero decir es que la primera prueba en el ejemplo anterior nunca fallará. Una prueba falla si se produce una excepción (y no se "atrapa" por el ExpectedExceptionAttribute)
Jonas
113
Mi método preferido para implementar esto es escribir un método llamado Throws 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; simplemente haga otro llamado MyAssert o algo similar. La clase se ve así:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace YourProject.Tests{publicstaticclassMyAssert{publicstaticvoidThrows<T>(Action func )where T :Exception{var exceptionThrown =false;try{
func.Invoke();}catch( T ){
exceptionThrown =true;}if(!exceptionThrown ){thrownewAssertFailedException(String.Format("An exception of type {0} was expected, but not thrown",typeof(T)));}}}}
Deshágase de la bandera de bool y ponga el tiro en la línea directamente después de la invocación para una implementación más compacta.
gt
11
Lo único que mejora esto es que la función devuelva la excepción capturada para que pueda continuar afirmando que cosas como los atributos de la excepción son correctos.
Mark Hildreth el
2
¡Gracias! Este parece ser el mejor enfoque para mí porque es una forma corta de probar múltiples excepciones en un método. También es mucho más legible.
David Sherret
2
Los atributos de @MickeyPerlstein rompen las reglas AAA para las pruebas. Específicamente, si su Arrange arroja la excepción incluso antes de llegar a la Ley, entonces su prueba pasa ... ¡eek!
Esto funciona, pero no lo recomiendo en general, ya que la lógica es demasiado complicada. No digo que sea complicado, pero considere si escribe este bloque de código para múltiples pruebas: 10s, 100s de pruebas. Esta lógica necesita ser desarrollada con un método de afirmación bien diseñado. Ver otras respuestas.
Steve
35
Tenga cuidado con el uso de ExpectedException, ya que puede conducir a varias dificultades como se demuestra aquí:
Si necesita probar excepciones, hay formas menos desaprobadas. Puede usar el método try {act / fail} catch {afirmar}, que puede ser útil para 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 unidad muy moderno, avanzado y extensible que ha aprendido de todos los demás errores y ha mejorado. Una de esas mejoras es Assert.Throws, que proporciona una sintaxis mucho mejor para afirmar excepciones.
La forma en que las pruebas unitarias se detienen para informarle sobre la excepción cuando usa ExpectedException me vuelve loco. ¿Por qué MS pensó que era una buena idea tener un paso manual en las pruebas automatizadas? Gracias por los enlaces.
Ant
@Ant: MS copió NUnit ... así que la verdadera pregunta es, ¿por qué NUnit pensó que era una buena idea?
jrista
28
MSTest (v2) ahora tiene una función Assert.ThrowsException que se puede usar así:
Assert.ThrowsException<System.FormatException>(()=>{Story actual =PersonalSite.Services.Content.ExtractHeader(String.Empty);});
Puedes instalarlo con nuget: Install-Package MSTest.TestFramework
publicstaticTExceptionAssertThrows<TException>(Action action)whereTException:Exception{try{
action();}catch(TException ex){return ex;}Assert.Fail("Expected exception was not thrown");returnnull;}
Puede descargar un paquete de 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 ensamblado y herede de BaseTest y puede usar la sintaxis Assert.Throws () .
El método principal para la implementación de Throws es el siguiente:
publicstaticvoidThrows<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(newException().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)));}}
Hay muchas otras respuestas, para mí estaba buscando una forma abreviada de probar las condiciones de falla conocidas solo por Tipo de excepción, esto hace que los casos de prueba sean más fáciles de leer. NOTA: el tipo de excepción no coincide con las clases de excepciones heredadas, como un try-catch estándar, por lo que el ejemplo anterior no atrapará un, ArgumentExceptionpor ejemplo. El viejo Try Catch y prueba la respuesta de excepción aún se prefiere si tiene criterios avanzados para probar, pero para muchos de mis casos, ¡esto ayuda mucho!
Chris Schaller
5
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. Esto es lo que escribí y uso.
publicstaticclassExceptionAssert{privatestatic T GetException<T>(Action action,string message="")where T :Exception{try{
action();}catch(T exception){return exception;}thrownewAssertFailedException("Expected exception "+typeof(T).FullName+", but none was propagated. "+ message);}publicstaticvoidPropagates<T>(Action action)where T :Exception{Propagates<T>(action,"");}publicstaticvoidPropagates<T>(Action action,string message)where T :Exception{GetException<T>(action, message);}publicstaticvoidPropagates<T>(Action action,Action<T> validation)where T :Exception{Propagates(action, validation,"");}publicstaticvoidPropagates<T>(Action action,Action<T> validation,string message)where T :Exception{
validation(GetException<T>(action, message));}}
Usos de ejemplo:
[TestMethod]publicvoidRun_PropagatesWin32Exception_ForInvalidExeFile(){(test setup that might propagate Win32Exception)ExceptionAssert.Propagates<Win32Exception>(()=>CommandExecutionUtil.Run(Assembly.GetExecutingAssembly().Location,newstring[0]));(more asserts or something)}[TestMethod]publicvoidRun_PropagatesFileNotFoundException_ForExecutableNotFound(){(test setup that might propagate FileNotFoundException)ExceptionAssert.Propagates<FileNotFoundException>(()=>CommandExecutionUtil.Run("NotThere.exe",newstring[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 de otras afirmaciones que uso.
A diferencia de otros, uso 'propaga' en lugar de 'arroja' ya que solo podemos probar si una excepción se propaga desde una llamada. No podemos probar directamente que se produzca una excepción. Pero supongo que podría significar tiros de imagen: arrojados 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 usar 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. Usar un enfoque consistente ahorra esfuerzo mental.
En resumen, este enfoque es deportivo: facilidad de uso, flexibilidad y robustez (difícil de hacer mal).
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{publicstaticclassMyAssert{/// <summary>/// Helper for Asserting that a function throws an exception of a particular type./// </summary>publicstaticvoidThrows<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){thrownewAssertFailedException(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
);}thrownewAssertFailedException(String.Format("An exception of type {0} was expected, but no exception was thrown.",typeof(T)));}}}}
Hmmm ... entiendo la idea, pero no estoy seguro de estar de acuerdo en que es mejor. El hecho de que queramos asegurarnos de que se genere una excepción específica no significa que todos los demás deban ser terminados como un fallo de afirmación. En mi humilde opinión, una excepción desconocida debería aumentar la pila como lo haría en cualquier otra operación de afirmación.
Crono
@Martin Quitaría el código que involucra la excepciónOtro y simplemente volvería a tirar de la segunda cláusula de captura
Tom Lint
4
Como menciona el uso de otras clases de prueba, una mejor opción que el ExpectedExceptionatributo es usar Shoudly 's should.Throw .
Digamos que tenemos un requisito de que el cliente debe tener una dirección para crear un pedido . Si no, el CreateOrderForCustomermétodo debería dar como resultado un ArgumentException. Entonces podríamos escribir:
[TestMethod]publicvoidNullUserIdInConstructor(){var customer =newCustomer(name :="Justin", address :=null};Should.Throw<ArgumentException>(()=>{var order =CreateOrderForCustomer(customer)});}
Esto es mejor que usar un ExpectedException atributo porque estamos siendo específicos sobre lo que debería arrojar 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.ThrowAsyncmétodo de prueba asincrónico.
En las pruebas de unidades integradas de VS si simplemente desea verificar que se produce "cualquier excepción", pero no conoce el tipo, puede usar un catch all:
Bueno, resumiré lo que todos los demás 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>publicstaticvoidAssertRaises<TException>(Action methodToExecute)whereTException: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.");}
Aunque esta es una vieja pregunta, me gustaría agregar un nuevo pensamiento a la discusión. He extendido el patrón Arrange, Act, Assert para que se espere, Arrange, Act, Assert. Puede hacer un puntero de excepción esperado y luego afirmar que se le asignó. Esto se siente más limpio que hacer sus afirmaciones en un bloque catch, dejando su sección Act principalmente solo por la línea de código para llamar al método bajo prueba. Tampoco tiene que hacerlo desde Assert.Fail();o returnhacia múltiples puntos en el código. Cualquier otra excepción lanzada hará que la prueba falle, porque no se detectará, y si se lanza una excepción de su tipo esperado, pero no fue la que esperaba, Afirmando contra el mensaje u otras propiedades de la excepción ayuda a asegurarse de que su prueba no pase inadvertidamente.
Respuestas:
Para "Prueba de equipo de Visual Studio", parece que aplica el atributo ExpectedException al método de prueba.
Muestra de la documentación aquí: Un recorrido de prueba de unidad con Visual Studio Team Test
fuente
Por lo general, su marco de prueba tendrá una respuesta para esto. Pero si no es lo suficientemente flexible, siempre puede hacer esto:
Como @Jonas señala, esto NO funciona para atrapar una excepción base:
Si absolutamente debe atrapar Exception, debe volver a lanzar Assert.Fail (). Pero realmente, esta es una señal de que no deberías escribir esto a mano; verifique su marco de prueba para ver las opciones, o vea si puede lanzar una excepción más significativa para probar.
Debería poder adaptar este enfoque a lo que quiera, incluida la especificación de qué tipos de excepciones se deben capturar. Si solo espera ciertos tipos, termine los
catch
bloques con:fuente
Mi método preferido para implementar esto es escribir un método llamado Throws 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; simplemente haga otro llamado MyAssert o algo similar. La clase se ve así:
Eso significa que su prueba de unidad se ve así:
Que se ve y se comporta mucho más como el resto de las sintaxis de prueba de unidad.
fuente
Assert.ThrowsException<T>
yAssert.ThrowsExceptionAsync<T>
- vea blogs.msdn.microsoft.com/visualstudioalm/2017/02/25/…Si usa NUNIT, puede hacer algo como esto:
También es posible almacenar la excepción lanzada para validarla aún más:
Ver: http://nunit.org/docs/2.5/exceptionAsserts.html
fuente
Si está utilizando MSTest, que originalmente no tenía un
ExpectedException
atributo, podría hacer esto:fuente
Tenga cuidado con el uso de ExpectedException, ya que puede conducir a varias dificultades 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 excepciones, hay formas menos desaprobadas. Puede usar el método try {act / fail} catch {afirmar}, que puede ser útil para 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 unidad muy moderno, avanzado y extensible que ha aprendido de todos los demás errores y ha mejorado. Una de esas mejoras es Assert.Throws, que proporciona una sintaxis mucho mejor para afirmar excepciones.
Puede encontrar xUnit.NET en github: http://xunit.github.io/
fuente
MSTest (v2) ahora tiene una función Assert.ThrowsException que se puede usar así:
Puedes instalarlo con nuget:
Install-Package MSTest.TestFramework
fuente
En un proyecto en el que estoy trabajando tenemos otra solución para hacer esto.
Primero, no me gusta el ExpectedExceptionAttribute porque toma en consideración qué método de llamada causó la Excepción.
Hago esto con un método auxiliar en su lugar.
Prueba
Método de ayuda
Aseado, no lo es;)
fuente
Es un atributo en el método de prueba ... no utiliza Assert. Se ve como esto:
fuente
Puede descargar un paquete de 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 ensamblado y herede de BaseTest y puede usar la sintaxis Assert.Throws () .
El método principal para la implementación de Throws es el siguiente:
Divulgación: preparé este paquete.
Más información: http://www.bradoncode.com/blog/2012/01/asserting-exceptions-in-mstest-with.html
fuente
Puede lograr esto con una simple línea.
Si su operación
foo.bar()
es asíncrona:Si
foo.bar()
no es asíncronofuente
ArgumentException
por ejemplo. El viejo Try Catch y prueba la respuesta de excepción aún se prefiere si tiene criterios avanzados para probar, pero para muchos de mis casos, ¡esto ayuda mucho!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. Esto es lo que escribí y uso.
Usos de ejemplo:
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 de otras afirmaciones que uso.
A diferencia de otros, uso 'propaga' en lugar de 'arroja' ya que solo podemos probar si una excepción se propaga desde una llamada. No podemos probar directamente que se produzca una excepción. Pero supongo que podría significar tiros de imagen: arrojados 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 usar 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. Usar un enfoque consistente ahorra esfuerzo mental.
En resumen, este enfoque es deportivo: facilidad de uso, flexibilidad y robustez (difícil de hacer mal).
fuente
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:
fuente
Como menciona el uso de otras clases de prueba, una mejor opción que el
ExpectedException
atributo es usar Shoudly 's should.Throw .Digamos que tenemos un requisito de que el cliente debe tener una dirección para crear un pedido . Si no, el
CreateOrderForCustomer
método debería dar como resultado unArgumentException
. Entonces podríamos escribir:Esto es mejor que usar un
ExpectedException
atributo porque estamos siendo específicos sobre lo que debería arrojar 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
método de prueba asincrónico.fuente
Como alternativa, puede probar las excepciones de prueba que de hecho se lanzan con las siguientes 2 líneas en su prueba.
fuente
En las pruebas de unidades integradas de VS si simplemente desea verificar que se produce "cualquier excepción", pero no conoce el tipo, puede usar un catch all:
fuente
Bueno, resumiré lo que todos los demás 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 ...
fuente
Consulte nUnit Docs para obtener ejemplos sobre:
fuente
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 obtener la excepción que realmente espera.
fuente
En caso de usar NUnit , intente esto:
fuente
Hay una biblioteca impresionante llamada NFluent que acelera y facilita la forma de escribir sus afirmaciones .
Es bastante sencillo escribir una afirmación para lanzar una excepción:
fuente
Aunque esta es una vieja pregunta, me gustaría agregar un nuevo pensamiento a la discusión. He extendido el patrón Arrange, Act, Assert para que se espere, Arrange, Act, Assert. Puede hacer un puntero de excepción esperado y luego afirmar que se le asignó. Esto se siente más limpio que hacer sus afirmaciones en un bloque catch, dejando su sección Act principalmente solo por la línea de código para llamar al método bajo prueba. Tampoco tiene que hacerlo desde
Assert.Fail();
oreturn
hacia múltiples puntos en el código. Cualquier otra excepción lanzada hará que la prueba falle, porque no se detectará, y si se lanza una excepción de su tipo esperado, pero no fue la que esperaba, Afirmando contra el mensaje u otras propiedades de la excepción ayuda a asegurarse de que su prueba no pase inadvertidamente.fuente