Тестирование методом асинхронного тестирования для конкретного исключения
Есть ли у кого-нибудь пример использования 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); });
}
но, конечно, тест не ждет завершения асинхронного метода и, следовательно, приводит к неудачному тесту, что исключение не было выбрано.
Ответы
Ответ 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); });
}