2012-10-11 126 views
12

有没有人有过如何在Windows 8 Metro应用程序中单元测试异步方法的例子,以确保它会抛出所需的异常?针对特定异常的单元测试异步方法

考虑一类具有异步方法

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

我想写使用新Async.ExpectsException施工的测试方法。我已经试过: -

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

当然,测试不等待异步方法来完成,因此,在一个失败的测试结果,确定异常尚未抛出的,但。

回答

14

您可以使用async Task单元测试与常规ExpectedExceptionAttribute:从评论

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

更新:对Win8的单元测试项目has been replacedAssert.ThrowsException,这是很好的无证AFAICTExpectedExceptionAttribute。这是a good change design-wise,但我不知道为什么它是只有支持Win8。

那么,假设没有async兼容Assert.ThrowsException(我不知道是否有一个或不是由于缺少文件),你可以自己建立一个:

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

,然后使用它:

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

请注意,我这里的示例只是对异常类型进行精确检查;你可能更喜欢允许后代类型。

更新2012-11-29:打开UserVoice suggestion将其添加到Visual Studio中。

+0

这个工作在C#的其他版本精,但已从Windows 8 metro应用程序http://blogs.msdn中删除。com/b/visualstudioalm/archive/2012/06/18/visual-studio-2012-rc-what-s-new-in-unit-testing.aspx – Peregrine

+0

那么,这是“有趣”!我已经更新了我的答案,以显示如果Win8单元测试不支持它,如何实现“异步”兼容的'ThrowsException'。 –

+0

谢谢斯蒂芬,确实有效,但我不敢相信这样一个优雅的新功能似乎需要这样的破解,尤其是看到Windows 8 Metro如此强调异步方法。希望Windows 8发布之后的最终文档将更加完善。 – Peregrine

1

几天前我遇到了类似的问题,最终创建了类似于斯蒂芬的回答上面的东西。它可以作为Gist。希望它有帮助--Github的要点在于具有完整的代码和示例用法。

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

支持使用ThrowsException方法内部异步lambda表达式从此been added in Visual Studio 2012 Update 2,但仅限于Windows应用商店的测试项目。

一个问题是,你需要使用Microsoft.VisualStudio.TestPlatform.UnitTestFramework.AppContainer.Assert类呼叫ThrowsException

因此,要使用新的ThrowsException方法,你可以做这样的事情:

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

从FluentAssertions NuGet包对我的作品使用.ShouldThrow()