Подтвердить что ты не робот

Тестирование методом асинхронного тестирования для конкретного исключения

Есть ли у кого-нибудь пример использования unit test метода асинхронизации в приложении 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); });
}

но, конечно, тест не ждет завершения асинхронного метода и, следовательно, приводит к неудачному тесту, что исключение не было выбрано.

4b9b3361

Ответ 1

Вы можете использовать async Task unit test с регулярным ExpectedExceptionAttribute:

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

Обновление от комментария: ExpectedExceptionAttribute в Win8 unit test проекты был заменен с помощью Assert.ThrowsException, который является красиво недокументированным AFAICT. Это хороший дизайн с изменением дизайна, но я не знаю, почему он поддерживается только на Win8.

Ну, полагая, что нет async -compatible 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, чтобы добавить это в Visual Studio.

Ответ 2

Я столкнулся с аналогичной проблемой несколько дней назад и в итоге создал нечто похожее на ответ Стивена выше. Он доступен как Gist. Надеюсь, это поможет - github gist at имеет полный код и пример использования.

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

Ответ 3

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

Использование .ShouldThrow() из FluentAssertions пакет nuget работает для меня

Ответ 4

Поддержка использования async lambdas внутри метода ThrowsException с тех пор была добавлена ​​в Visual Studio 2012 Update 2, но только для теста Windows Store проектов.

Один из них - это то, что вам нужно использовать класс 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); });
}