У меня есть следующие четыре теста, и последний зависает, когда я его запускаю. Почему это происходит:
[Test]
public void CheckOnceResultTest()
{
Assert.IsTrue(CheckStatus().Result);
}
[Test]
public async void CheckOnceAwaitTest()
{
Assert.IsTrue(await CheckStatus());
}
[Test]
public async void CheckStatusTwiceAwaitTest()
{
Assert.IsTrue(await CheckStatus());
Assert.IsTrue(await CheckStatus());
}
[Test]
public async void CheckStatusTwiceResultTest()
{
Assert.IsTrue(CheckStatus().Result); // This hangs
Assert.IsTrue(await CheckStatus());
}
private async Task<bool> CheckStatus()
{
var restClient = new RestClient(@"https://api.test.nordnet.se/next/1");
Task<IRestResponse<DummyServiceStatus>> restResponse = restClient.ExecuteTaskAsync<DummyServiceStatus>(new RestRequest(Method.GET));
IRestResponse<DummyServiceStatus> response = await restResponse;
return response.Data.SystemRunning;
}
Я использую этот метод расширения для restsharp RestClient :
public static class RestClientExt
{
public static Task<IRestResponse<T>> ExecuteTaskAsync<T>(this RestClient client, IRestRequest request) where T : new()
{
var tcs = new TaskCompletionSource<IRestResponse<T>>();
RestRequestAsyncHandle asyncHandle = client.ExecuteAsync<T>(request, tcs.SetResult);
return tcs.Task;
}
}
public class DummyServiceStatus
{
public string Message { get; set; }
public bool ValidVersion { get; set; }
public bool SystemRunning { get; set; }
public bool SkipPhrase { get; set; }
public long Timestamp { get; set; }
}
Почему зависает последний тест?
async void
методы модульного тестирования; они просто не будут работать. Однако NUnit это делает. Тем не менее, я согласен с общим принципом предпочтенияasync Task
большемуasync void
.Ответы:
Вы сталкиваетесь со стандартной ситуацией взаимоблокировки, которую я описываю в своем блоге и в статье MSDN :
async
метод пытается запланировать свое продолжение в потоке, который блокируется вызовомResult
.В этом случае ваш
SynchronizationContext
- тот, который используется NUnit для выполненияasync void
тестовых методов.async Task
Вместо этого я бы попробовал использовать методы тестирования.источник
async
полностью» (как отмечено в моей статье MSDN). Другими словами, как гласит заголовок моего сообщения в блоге, «не блокируйте асинхронный код».Wait()
на создание метода вызоваasync
. Но мне кажется, что это подталкивает проблему вверх по течению. В какой-то момент чем-то нужно управлять синхронно. Что, если моя функция целенаправленно синхронна, потому что она управляет долго выполняющимися рабочими потокамиTask.Run()
? Как мне дождаться завершения этого теста без взаимоблокировки внутри моего теста NUnit?At some point, something has to be managed synchronously.
- совсем нет. Для приложений пользовательского интерфейса точка входа может бытьasync void
обработчиком событий. Для серверных приложений точкой входа может бытьasync Task<T>
действие. Предпочтительно использоватьasync
оба, чтобы избежать блокировки потоков. Вы можете сделать ваш тест NUnit синхронным или асинхронным; если async, сделайте этоasync Task
вместоasync void
. Если он синхронный, то не должно быть,SynchronizationContext
поэтому не должно быть тупика.Получение значения с помощью асинхронного метода:
Синхронный вызов асинхронного метода
Никаких проблем с взаимоблокировкой не возникнет из-за использования Task.Run.
источник
async void
модульного тестирования и удаление гарантий того же потока, предоставляемых классомSynchronizationContext
из тестируемой системы..GetAwaiter().GetResult()
вместо этого,.Result
чтобы ничегоException
не было упаковано.Вы можете избежать тупика, добавив
ConfigureAwait(false)
в эту строку:=>
Я описал эту ловушку в своем блоге. Ловушки async / await.
источник
Вы блокируете пользовательский интерфейс с помощью свойства Task.Result. В документации MSDN четко указано, что
Лучшим решением для этого сценария было бы удалить как await, так и async из методов и использовать только Task, в котором вы возвращаете результат. Это не нарушит вашу последовательность выполнения.
источник
Если вы не получаете никаких обратных вызовов или элемент управления зависает после вызова асинхронной функции службы / API, вам необходимо настроить Context для возврата результата в том же вызываемом контексте.
использование
TestAsync().ConfigureAwait(continueOnCapturedContext: false);
Вы столкнетесь с этой проблемой только в веб-приложениях, но не в
static void main
.источник
ConfigureAwait
позволяет избежать взаимоблокировки в определенных сценариях, не работая в исходном контексте потока.