2012-10-11 33 views
12

¿Alguien tiene un ejemplo de cómo probar un método asincrónico en una aplicación Metro de Windows 8, para asegurarse de que lanza la excepción requerida?Método asincrónico de prueba unitaria para excepción específica

dado una clase con un método asíncrono

public static class AsyncMathsStatic 
{ 
    private const int DELAY = 500; 

    public static async Task<int> Divide(int A, int B) 
    { 
     await Task.Delay(DELAY); 
     if (B == 0) 
      throw new DivideByZeroException(); 
     else 
      return A/B; 
    } 
} 

Quiero escribir un método de ensayo utilizando la nueva construcción Async.ExpectsException. He intentado: -

[TestMethod] 
public void DivideTest1() 
{ 
    Assert.ThrowsException<DivideByZeroException>(async() => { int Result = await AsyncMathsStatic.Divide(4, 0); }); 
} 

pero por supuesto la prueba no espera a que el método asíncrono para completar, y así resulta en una prueba fallida que la excepción no ha sido lanzado.

Respuesta

14

Usted puede utilizar una prueba async Task unidad con la regularidad ExpectedExceptionAttribute:

[TestMethod] 
[ExpectedException(typeof(DivideByZeroException))] 
public async Task DivideTest1() 
{ 
    int Result = await AsyncMathsStatic.Divide(4, 0); 
} 

actualización de comentario:ExpectedExceptionAttribute en proyectos de prueba de unidad Win8 has been replaced con Assert.ThrowsException, que está muy bien documentada AFAICT. Esto es a good change design-wise, pero no sé por qué es solo compatible con Win8.

Bueno, suponiendo que no hay async compatible Assert.ThrowsException (no puedo decir si hay es uno o no, debido a la falta de documentación), se podría construir uno usted mismo:

public static class AssertEx 
{ 
    public async Task ThrowsExceptionAsync<TException>(Func<Task> code) 
    { 
    try 
    { 
     await code(); 
    } 
    catch (Exception ex) 
    { 
     if (ex.GetType() == typeof(TException)) 
     return; 
     throw new AssertFailedException("Incorrect type; expected ... got ...", ex); 
    } 

    throw new AssertFailedException("Did not see expected exception ..."); 
    } 
} 

y luego Úselo como tal:

[TestMethod] 
public async Task DivideTest1() 
{ 
    await AssertEx.ThrowsException<DivideByZeroException>(async() => { 
     int Result = await AsyncMathsStatic.Divide(4, 0); 
    }); 
} 

Tenga en cuenta que mi ejemplo aquí solo está haciendo una comprobación exacta para el tipo de excepción; es posible que prefiera permitir también tipos descendientes.

Actualización 2012-11-29: Se abrió un UserVoice suggestion para agregar esto a Visual Studio.

+0

Esto funciona bien en otras versiones de C# , pero se ha eliminado de las aplicaciones de Metro de Windows 8 http: //blogs.msdn.com/b/visualstudioalm/archive/2012/06/18/visual-studio-2012-rc-what-s-new-in-unit-testing.aspx – Peregrine

+0

¡Bueno, eso es "interesante"! He actualizado mi respuesta para mostrar cómo se puede implementar un 'ThrowsException' compatible con' async' si las pruebas de la unidad Win8 no lo admiten. –

+0

Gracias Stephen que funciona, aunque no puedo creer que lo que parece ser una nueva característica tan elegante requiera tal hack, especialmente teniendo en cuenta que Windows 8 Metro pone tanto énfasis en los métodos asíncronos. Afortunadamente, la documentación final posterior al lanzamiento de Windows 8 será más próxima. – Peregrine

1

Me encontré con un problema similar hace unos días y terminé creando algo similar a la respuesta de Stephen anterior. Está disponible como Gist. Con suerte, es de ayuda; el github gist tiene el código completo y un uso de muestra.

/// <summary> 
/// Async Asserts use with Microsoft.VisualStudio.TestPlatform.UnitTestFramework 
/// </summary> 
public static class AsyncAsserts 
{ 
    /// <summary> 
    /// Verifies that an exception of type <typeparamref name="T"/> is thrown when async<paramref name="func"/> is executed. 
    /// The assertion fails if no exception is thrown 
    /// </summary> 
    /// <typeparam name="T">The generic exception which is expected to be thrown</typeparam> 
    /// <param name="func">The async Func which is expected to throw an exception</param> 
    /// <returns>The task object representing the asynchronous operation.</returns> 
    public static async Task<T> ThrowsException<T>(Func<Task> func) where T : Exception 
    { 
     return await ThrowsException<T>(func, null); 
    } 

    /// <summary> 
    /// Verifies that an exception of type <typeparamref name="T"/> is thrown when async<paramref name="func"/> is executed. 
    /// The assertion fails if no exception is thrown 
    /// </summary> 
    /// <typeparam name="T">The generic exception which is expected to be thrown</typeparam> 
    /// <param name="func">The async Func which is expected to throw an exception</param> 
    /// <param name="message">A message to display if the assertion fails. This message can be seen in the unit test results.</param> 
    /// <returns>The task object representing the asynchronous operation.</returns> 
    public static async Task<T> ThrowsException<T>(Func<Task> func, string message) where T : Exception 
    { 
     if (func == null) 
     { 
      throw new ArgumentNullException("func"); 
     } 

     string failureMessage; 
     try 
     { 
      await func(); 
     } 
     catch (Exception exception) 
     { 
      if (!typeof(T).Equals(exception.GetType())) 
      { 
       // "Threw exception {2}, but exception {1} was expected. {0}\nException Message: {3}\nStack Trace: {4}" 
       failureMessage = string.Format(
        CultureInfo.CurrentCulture, 
        FrameworkMessages.WrongExceptionThrown, 
        message ?? string.Empty, 
        typeof(T), 
        exception.GetType().Name, 
        exception.Message, 
        exception.StackTrace); 

       Fail(failureMessage); 
      } 
      else 
      { 
       return (T)exception; 
      } 
     } 

     // "No exception thrown. {1} exception was expected. {0}" 
     failureMessage = string.Format(
        CultureInfo.CurrentCulture, 
        FrameworkMessages.NoExceptionThrown, 
        message ?? string.Empty, 
        typeof(T)); 

     Fail(failureMessage); 
     return default(T); 
    } 

    private static void Fail(string message, [CallerMemberName] string assertionName = null) 
    { 
     string failureMessage = string.Format(
      CultureInfo.CurrentCulture, 
      FrameworkMessages.AssertionFailed, 
      assertionName, 
      message); 

     throw new AssertFailedException(failureMessage); 
    } 
} 
0

Soporte para el uso lambdas asíncronas dentro de un método ThrowsException tiene desde been added in Visual Studio 2012 Update 2, pero sólo para los proyectos de prueba de Windows Store.

El único problema es que debe utilizar la clase Microsoft.VisualStudio.TestPlatform.UnitTestFramework.AppContainer.Assert para llamar al ThrowsException.

lo tanto, para utilizar el nuevo método throwsException, se podría hacer algo como esto:

using AsyncAssert = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.AppContainer.Assert; 

[TestMethod] 
public void DivideTest1() 
{ 
    AsyncAssert.ThrowsException<DivideByZeroException>(async() => { 
     int Result = await AsyncMathsStatic.Divide(4, 0); }); 
} 
1
[TestMethod] 
public void DivideTest1() 
{ 
    Func<Task> action = async() => { int Result = await AsyncMathsStatic.Divide(4, 0); }); 
    action.ShouldThrow<DivideByZeroException>(); 
} 

Usando .ShouldThrow() de FluentAssertions paquete Nuget funciona para mí

Cuestiones relacionadas