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

Как проверить асинхронный метод с помощью NUnit (или, возможно, с другой структурой)?

У меня есть приложение ASP.NET Web API с ApiController, в котором есть асинхронные методы, возвращающие объекты Task<> и помеченные ключевым словом async.

public class MyApiController : ApiController
{
    public async Task<MyData> GetDataById(string id)
    {
        ...
    }
}

Как я могу написать тесты NUnit для асинхронных методов ApiController? Если мне нужно использовать другую структуру тестирования, я тоже открыт для этого. Я новичок в тестировании модулей .NET в целом, поэтому я заинтересован в изучении лучших практик.

4b9b3361

Ответ 1

Мне кажется, что в NUnit 2.6 нет поддержки для тестирования методов async, возвращающих Tasks. Лучший вариант, который я могу увидеть прямо сейчас, - использовать собственную инфраструктуру UnitTesting для Visual Studio или xUnit.net как поддержку асинхронных тестов.

С инфраструктурой Visual Studio UnitTesting я могу написать асинхронные тесты следующим образом:

using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class TestAsyncMethods
{
    [TestMethod]
    public async Task TestGetBinBuildById()
    {
         ...
         var rslt = await obj.GetAsync();
         Assert.AreEqual(rslt, expected);
    }
}

Ответ 2

На сегодняшний день (7/2/2014) асинхронное тестирование поддерживается:

В первых двух средах тестовый метод должен иметь эту подпись:

[TestMethod]
public async Task MyTestMethod()
{
   ...
   var result = await objectUnderTest.TestedAsyncMethod(...);
   // Make assertions
}

NUnit, помимо этой подписи, поддерживает этот:

public async void MyTestMethod()

Конечно, в любом из этих методов тестирования вы можете использовать await для вызова и ожидания асинхронных методов.

Если вы используете инфраструктуру тестирования, которая не поддерживает методы тестирования async, тогда единственный способ сделать это - вызвать метод async и подождать, пока он закончит работу с использованием любого из обычных способов: await, считывая свойство Result Task<T>, возвращаемое методом async, используя любой из обычных методов ожидания Task и так далее. После ожидания, вы можете делать все утверждения, как обычно. Например, используя MSTest:

[TestMethod]
public void MyTestMethod()
{
    ...
    Task<MyResultClass> task = objectUnderTest.MyAsyncMethod(...);
    // Make anything that waits for the method to end
    MyResultClass result = task.Result;

    // Make the assertions
    Assert.IsNotNull(result);
    ...
}

Ответ 3

Имейте в виду, что серия NUnit 2.6, как и предыдущие, построена так, чтобы ориентироваться на платформу .NET 2.0. Таким образом, NUnit может выполнять только те же действия, которые вы могли бы кодировать самостоятельно, в сборке, ориентированной на .NET 2.0.

В частности, вы не можете пометить свой тестовый метод как асинхронный и ожидать, что NUnit сделает что-то особенное с ним.

Вы можете, однако,

  • Цель .NET 4.5 для ваших тестов. NUnit будет запускать их в отдельном обработать.
  • Используйте ожидание в своем тесте, чтобы дождаться результата вызова асинхронный метод.

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

Другой вариант - использовать NUnitLite. NUnitLite 0.8 поддерживает атрибут [Асинхронный], который позволит продолжить тестирование, пока ваш асинхронный тест завершен. Преимущество атрибута заключается в том, что он позволяет асинхронным тестам работать с .NET 2.0 до 4.0

В настоящее время у нас нет сборки .NET 4.5 NUnitLite, но она будет добавлена ​​в ближайшее время, и мы будем работать над изменением, которое будет использовать [Asynchronous] в этой среде. На данный момент вы можете легко загрузить и перекомпилировать исходный код для .NET 4.5.

В будущем, посмотрите на NUnit 3.0, чтобы полностью поддерживать методы async наряду с общим параллельным выполнением тестов для нескольких потоков или в нескольких процессах.

Ответ 4

Это действительно зависит от того, что они делают. Обычно они будут зависеть от чего-то другого, что обеспечивает Task<T> или что-то подобное. В этом случае вы можете предоставить фальшивые зависимости, которые позволят вам контролировать все это мелким способом. У меня есть прототип "машины времени", который позволяет предварительно запрограммировать задачи для завершения в определенные искусственные времена, затем перемещать время вперед и выполнять утверждения по мере продвижения. Там есть сообщение в блоге об этом, которое может показаться вам полезным. Как я уже сказал, это только прототип, и он не подходит для всех сценариев - но он может вам подойдет.

У Стивена Клири также есть пара сообщений в блоге об модульном тестировании (1, 2), используя несколько иной подход, а также пакет NuGet могут оказаться полезными.

Базовый подход такой же, как и в обычном режиме: дайте вашему методу разные входы (и выходы зависимостей) и проверьте результаты. Это определенно более сложное достижение с асинхронностью, но это выполнимо.

Ответ 5

Я пытаюсь преобразовать некоторые из моих методов в async. Получение этого для работы с NUnit было довольно простым.

Методы испытаний не могут быть асинхронными. Но у нас все еще есть доступ к полной функциональности параллельной библиотеки задач, мы просто не можем использовать ключевое слово await непосредственно в методе тестирования.

В моем примере у меня был метод:

    public string SendUpdateRequestToPlayer(long playerId)

И он был протестирован в nUnit так:

     string result = mgr.SendUpdateRequestToPlayer(player.Id.Value);
     Assert.AreEqual("Status update request sent", result);
     mocks.VerifyAll();

Теперь, когда я изменил метод SendUpdateRequestToPlayer как асинхронный

      public async Task<string> SendUpdateRequestToPlayer(long playerId)

Мне просто пришлось изменить мои тесты на Wait для завершения задачи:

 Task<string> task = mgr.SendUpdateRequestToPlayer(player.Id.Value);
 task.Wait(); // The task runs to completion on a background thread
 Assert.AreEqual("Status update request sent", task.Result);
 mocks.VerifyAll();