usuario tipos sintaxis que programacion por para net más manejo generar existen excepciones excepcion específicos errores elevar definidas comunes como capturar c# .net unit-testing

c# - tipos - La mejor forma de probar excepciones con Assert para garantizar que se lanzarán



sintaxis para el manejo de excepciones en c# (8)

A partir de v 2.5, NUnit tiene las siguientes Assert nivel de método para probar excepciones:

Assert.Throws , que probará un tipo de excepción exacto:

Assert.Throws<NullReferenceException>(() => someNullObject.ToString());

Y Assert.Catch , que probará una excepción de un tipo determinado, o un tipo de excepción derivado de este tipo:

Assert.Catch<Exception>(() => someNullObject.ToString());

Como comentario adicional, al depurar las pruebas unitarias que generan excepciones, es posible que desee evitar que VS rompa la excepción .

Editar

Solo para dar un ejemplo del comentario de Matthew a continuación, la devolución del genérico Assert.Throws and Assert.Catch es la excepción con el tipo de la excepción, que luego puede examinar para una inspección adicional:

// The type of ex is that of the generic type parameter (SqlException) var ex = Assert.Throws<SqlException>(() => MethodWhichDeadlocks()); Assert.AreEqual(1205, ex.Number);

¿Crees que esta es una buena forma de probar excepciones? ¿Alguna sugerencia?

Exception exception = null; try{ //I m sure that an exeption will happen here } catch (Exception ex){ exception = ex; } Assert.IsNotNull(exception);

Estoy usando MS Test.


Ahora, 2017, puede hacerlo más fácil con el nuevo Framework MSTest V2 :

Assert.ThrowsException<Exception>(() => myClass.MyMethodWithError());


Como alternativa al uso del atributo ExpectedException , a veces defino dos métodos útiles para mis clases de prueba:

AssertThrowsException() toma un delegado y afirma que arroja la excepción esperada con el mensaje esperado.

AssertDoesNotThrowException() toma el mismo delegado y afirma que no lanza una excepción.

Este emparejamiento puede ser muy útil cuando quiere probar que se arroja una excepción en un caso, pero no en el otro.

Utilizándolos, mi código de prueba de unidad podría verse así:

ExceptionThrower callStartOp = delegate(){ testObj.StartOperation(); }; // Check exception is thrown correctly... AssertThrowsException(callStartOp, typeof(InvalidOperationException), "StartOperation() called when not ready."); testObj.Ready = true; // Check exception is now not thrown... AssertDoesNotThrowException(callStartOp);

Agradable y aseado ¿eh?

Mis AssertThrowsException() y AssertDoesNotThrowException() se definen en una clase base común de la siguiente manera:

protected delegate void ExceptionThrower(); /// <summary> /// Asserts that calling a method results in an exception of the stated type with the stated message. /// </summary> /// <param name="exceptionThrowingFunc">Delegate that calls the method to be tested.</param> /// <param name="expectedExceptionType">The expected type of the exception, e.g. typeof(FormatException).</param> /// <param name="expectedExceptionMessage">The expected exception message (or fragment of the whole message)</param> protected void AssertThrowsException(ExceptionThrower exceptionThrowingFunc, Type expectedExceptionType, string expectedExceptionMessage) { try { exceptionThrowingFunc(); Assert.Fail("Call did not raise any exception, but one was expected."); } catch (NUnit.Framework.AssertionException) { // Ignore and rethrow NUnit exception throw; } catch (Exception ex) { Assert.IsInstanceOfType(expectedExceptionType, ex, "Exception raised was not the expected type."); Assert.IsTrue(ex.Message.Contains(expectedExceptionMessage), "Exception raised did not contain expected message. Expected=/"" + expectedExceptionMessage + "/", got /"" + ex.Message + "/""); } } /// <summary> /// Asserts that calling a method does not throw an exception. /// </summary> /// <remarks> /// This is typically only used in conjunction with <see cref="AssertThrowsException"/>. (e.g. once you have tested that an ExceptionThrower /// method throws an exception then your test may fix the cause of the exception and then call this to make sure it is now fixed). /// </remarks> /// <param name="exceptionThrowingFunc">Delegate that calls the method to be tested.</param> protected void AssertDoesNotThrowException(ExceptionThrower exceptionThrowingFunc) { try { exceptionThrowingFunc(); } catch (NUnit.Framework.AssertionException) { // Ignore and rethrow any NUnit exception throw; } catch (Exception ex) { Assert.Fail("Call raised an unexpected exception: " + ex.Message); } }


Con la mayoría de los frameworks de pruebas de unidades .net puede poner un atributo [ExpectedException] en el método de prueba. Sin embargo, esto no puede decirle que la excepción ocurrió en el momento en que usted lo esperaba. Ahí es donde xunit.net puede ayudar.

Con xunit tienes Assert.Throws, entonces puedes hacer cosas como esta:

[Fact] public void CantDecrementBasketLineQuantityBelowZero() { var o = new Basket(); var p = new Product {Id = 1, NetPrice = 23.45m}; o.AddProduct(p, 1); Assert.Throws<BusinessException>(() => o.SetProductQuantity(p, -3)); }

[Hecho] es el equivalente de xunit de [TestMethod]


Desafortunadamente MSTest STILL solo tiene realmente el atributo ExpectedException (solo muestra cuánto le importa MS a MSTest) que IMO es bastante horrible porque rompe el patrón Arrange / Act / Assert y no le permite especificar exactamente qué línea de código espera la excepción para ocurrir en.

Cuando estoy usando (/ forzado por un cliente) para usar MSTest siempre utilizo esta clase de ayuda:

public static class AssertException { public static void Throws<TException>(Action action) where TException : Exception { try { action(); } catch (Exception ex) { Assert.IsTrue(ex.GetType() == typeof(TException), "Expected exception of type " + typeof(TException) + " but type of " + ex.GetType() + " was thrown instead."); return; } Assert.Fail("Expected exception of type " + typeof(TException) + " but no exception was thrown."); } public static void Throws<TException>(Action action, string expectedMessage) where TException : Exception { try { action(); } catch (Exception ex) { Assert.IsTrue(ex.GetType() == typeof(TException), "Expected exception of type " + typeof(TException) + " but type of " + ex.GetType() + " was thrown instead."); Assert.AreEqual(expectedMessage, ex.Message, "Expected exception with a message of ''" + expectedMessage + "'' but exception with message of ''" + ex.Message + "'' was thrown instead."); return; } Assert.Fail("Expected exception of type " + typeof(TException) + " but no exception was thrown."); } }

Ejemplo de uso:

AssertException.Throws<ArgumentNullException>(() => classUnderTest.GetCustomer(null));


Marque la prueba con ExpectedExceptionAttribute (este es el término en NUnit o MSTest; es posible que los usuarios de otros marcos de pruebas de unidades necesiten traducir).


Soy nuevo aquí y no tengo la reputación de comentar o de votar negativamente, pero quería señalar un error en el ejemplo de la respuesta de Andy White :

try { SomethingThatCausesAnException(); Assert.Fail("Should have exceptioned above!"); } catch (Exception ex) { // whatever logging code }

En todos los marcos de pruebas unitarias con los que estoy familiarizado, Assert.Fail funciona arrojando una excepción, por lo que la captura genérica realmente enmascarará la falla de la prueba. Si SomethingThatCausesAnException() no arroja, el Assert.Fail hará, pero eso nunca pasará al corredor de prueba para indicar la falla.

Si necesita detectar la excepción esperada (es decir, para afirmar ciertos detalles, como el mensaje / las propiedades de la excepción), es importante detectar el tipo esperado específico, y no la clase de excepción base. Eso permitiría que la excepción Assert.Fail salga burbujeante (suponiendo que no esté lanzando el mismo tipo de excepción que el marco de prueba de su unidad), pero aún así permita la validación de la excepción lanzada por su método SomethingThatCausesAnException() .


Tengo un par de patrones diferentes que uso. Uso el atributo ExpectedException mayor parte del tiempo cuando se espera una excepción. Esto es suficiente para la mayoría de los casos, sin embargo, hay algunos casos en que esto no es suficiente. Es posible que la excepción no sea atrapable, ya que es lanzada por un método invocado por reflexión, o tal vez solo quiero verificar que se cumplan otras condiciones, digamos que una transacción se revierte o aún se ha establecido algún valor. En estos casos, lo Assert.Fail en un bloque try/catch que espera la excepción exacta, hace un Assert.Fail si el código tiene éxito y también Assert.Fail excepciones genéricas para asegurarse de que no se lanza una excepción diferente.

Primer caso:

[TestMethod] [ExpectedException(typeof(ArgumentNullException))] public void MethodTest() { var obj = new ClassRequiringNonNullParameter( null ); }

Segundo caso:

[TestMethod] public void MethodTest() { try { var obj = new ClassRequiringNonNullParameter( null ); Assert.Fail("An exception should have been thrown"); } catch (ArgumentNullException ae) { Assert.AreEqual( "Parameter cannot be null or empty.", ae.Message ); } catch (Exception e) { Assert.Fail( string.Format( "Unexpected exception of type {0} caught: {1}", e.GetType(), e.Message ) ); } }