2012-10-11 3 views
12

Est-ce que quelqu'un a un exemple de test unitaire d'une méthode asynchrone dans une application Metro de Windows 8, pour s'assurer qu'elle déclenche l'exception requise?Méthode d'asynchrone de test unitaire pour une exception spécifique

une classe avec Étant donné une méthode async

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

Je veux écrire une méthode d'essai en utilisant la nouvelle construction de Async.ExpectsException. J'ai essayé: -

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

mais bien sûr, le test n'attend pas la méthode async pour compléter, et ainsi de résultats dans un test échoué que l'exception n'a pas été lancé.

Répondre

14

Vous pouvez utiliser un test unitaire async Task avec le ExpectedExceptionAttribute régulier:

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

Mise à jour du commentaire:ExpectedExceptionAttribute sur des projets de tests unitaires avec Win8 has been replacedAssert.ThrowsException, ce qui est bien AFAICT sans papier. C'est a good change design-wise, mais je ne sais pas pourquoi c'est seulement pris en charge sur Win8.

Eh bien, en supposant qu'il n'y a pas asyncAssert.ThrowsException compatible (je ne peux pas dire s'il est un ou non en raison du manque de documentation), vous pouvez construire un vous-même:

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

puis utilisez-le comme tel:

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

Notez que mon exemple ici ne fait que vérifier le type d'exception; vous pouvez également autoriser les types de descendants.

Mise à jour 2012-11-29: Ouverture d'un UserVoice suggestion pour l'ajouter à Visual Studio.

+0

Cela fonctionne très bien dans d'autres versions de C# , mais a été retiré de Windows 8 applications métro http: //blogs.msdn.com/b/visualstudioalm/archive/2012/06/18/visual-studio-2012-rc-quoi-s-new-in-unit-testing.aspx – Peregrine

+0

Eh bien, c'est "intéressant"! J'ai mis à jour ma réponse pour montrer comment vous pouvez implémenter une 'ThrowsException 'compatible' async' si les tests unitaires Win8 ne le supportent pas. –

+0

Merci Stephen qui fonctionne, même si je ne peux pas croire que ce qui semble être une nouvelle fonctionnalité aussi élégante nécessiterait un tel hack, d'autant plus que Windows 8 Metro met un tel accent sur les méthodes asynchrones. Espérons que la documentation finale suivant la version de Windows 8 sera plus à jour. – Peregrine

1

J'ai rencontré un problème similaire il y a quelques jours et j'ai fini par créer quelque chose de similaire à la réponse de Stephen plus haut. Il est disponible en tant que Gist. J'espère que c'est utile - le github gist a le code complet et un exemple d'utilisation.

/// <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

Support de l'utilisation lambdas async intérieur d'une méthode ThrowsException a depuis been added in Visual Studio 2012 Update 2, mais uniquement pour les projets de test Windows Store.

L'un obtenu est que vous devez utiliser la classe Microsoft.VisualStudio.TestPlatform.UnitTestFramework.AppContainer.Assert pour appeler ThrowsException.

Donc, pour utiliser la nouvelle méthode de ThrowsException, vous pouvez faire quelque chose comme ceci:

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

En utilisant .ShouldThrow() de FluentAssertions package NuGet fonctionne pour moi

Questions connexes