2012-10-11 170 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()