Обложка канала

.NET Разработчик

Опытный разработчик не так давно зашёл в .Net и поставил цель получить сертификат Microsoft. Свой ежедневный прогресс он описывает на канале .Net Разработчик. Заметки об изученном материале, советы по повышению производительности и поддержке мотивации, ин

.NET Разработчик

4 года назад
Открыть в
День 1136. #ЗаметкиНаПолях #AsyncTips Модульное тестирование async-методов Задача: Имеется async-метод, для которого необходимо провести модульное тестирование. Решение Многие современные фреймворки модульного тестирования, включая MSTest, NUnit и xUnit, поддерживают методы модульного тестирования async Task. Пример асинхронного модульного теста в MSTest:
[TestMethod]
public async Task MethodAsync_False()
{
  var sut = …;
  var result = await sut.MethodAsync();
  Assert.IsFalse(result);
}

Фреймворк модульного тестирования замечает, что метод возвращает Task, и ожидает завершения задачи перед тем, как сделать отметку о прохождении или отказе теста. Если ваш фреймворк модульного тестирования не поддерживает модульные тесты с async Task, то ему придётся помочь с ожиданием тестируемой асинхронной операции. Один из вариантов — использовать GetAwaiter().GetResult() для синхронного блокирования по задаче. Использование GetAwaiter().GetResult() вместо Wait() позволит избежать обёртки в AggregateException, когда в задаче произойдёт исключение. Имитация (mocking) асинхронных зависимостей на первый взгляд кажется немного неуклюжей. Всегда желательно хотя бы проверить, как ваши методы реагируют на синхронный успех (имитация с Task.FromResult), синхронные ошибки (имитация с Task.FromException) и асинхронный успех (имитация с Task.Yield и возвратом значения). Task.FromResult и Task.FromException рассматривались здесь. Task.Yield может использоваться для принудительного применения асинхронного поведения и задействуется прежде всего при модульном тестировании:
interface IMyInterface
{
  Task<int> SomeAsync();
}
class SyncSuccess : IMyInterface
{
  public Task<int> SomeAsync()
  {
    return Task.FromResult(42);
  }
}

class SyncError : IMyInterface
{
  public Task<int> SomeAsync()
  {
    return Task.FromException<int>(
      new InvalidOperationException());
  }
}

class AsyncSuccess : IMyInterface
{
  public async Task<int> SomeAsync()
  {
    // Принудительно включаем асинхронное поведение
    await Task.Yield();
    return 42;
  }
}

При тестировании асинхронного кода взаимоблокировки и состояния гонки могут проявляться чаще, чем при тестировании синхронного кода. Может быть полезным назначение тайм-аута тестам. Тестовому методу можно задать атрибут Timeout(int timeOut), передав ему количество миллисекунд таймаута. Значение по умолчанию - TestTimeout.Infinite ("бесконечно"). Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 7.