Eu tenho os quatro testes a seguir e o último trava quando eu o executo. Por que isso acontece:
[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;
}
Eu uso esse método de extensão para o 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; }
}
Por que o último teste travou?
async void
métodos de teste de unidade; eles simplesmente não funcionam. No entanto, o NUnit faz. Dito isto, estou de acordo com o princípio geral de preferirasync Task
maisasync void
.Respostas:
Você está enfrentando a situação padrão de conflito que eu descrevo no meu blog e em um artigo do MSDN : o
async
método está tentando agendar sua continuação em um segmento que está sendo bloqueado pela chamada paraResult
.Nesse caso,
SynchronizationContext
é o seu usado pelo NUnit para executarasync void
métodos de teste. Eu tentaria usarasync Task
métodos de teste.fonte
async
todo o caminho" (conforme observado no meu artigo do MSDN). Em outras palavras - como diz o título da minha postagem no blog - "não bloqueie no código assíncrono".Wait()
o método de chamadaasync
. Mas para mim, isso parece estar levando o problema a montante. Em algum momento, algo precisa ser gerenciado de forma síncrona. E se minha função for propositalmente síncrona porque gerencia com threads de trabalho de longa execuçãoTask.Run()
? Como espero que isso termine sem conflito dentro do meu teste NUnit?At some point, something has to be managed synchronously.
- de maneira alguma. Para aplicativos de interface do usuário, o ponto de entrada pode ser umasync void
manipulador de eventos. Para aplicativos de servidor, o ponto de entrada pode ser umaasync Task<T>
ação. É preferível usar osasync
dois para evitar o bloqueio de threads. Você pode fazer com que seu teste NUnit seja síncrono ou assíncrono; se assíncrono, faça-o emasync Task
vez deasync void
. Se for síncrono, não deve ter um,SynchronizationContext
então não deve haver um impasse.Adquirindo um valor por meio de um método assíncrono:
Chamar um método assíncrono de forma síncrona
Nenhum problema de conflito ocorrerá devido ao uso do Task.Run.
fonte
async void
métodos de teste de unidade e remover as garantias de mesma rosca fornecidas peloSynchronizationContext
sistema do teste..GetAwaiter().GetResult()
em vez de.Result
modo que qualquerException
não está embrulhado.Você pode evitar o impasse adicionando
ConfigureAwait(false)
a esta linha:=>
Eu descrevi essa armadilha no meu blog Pitfalls of async / waitit
fonte
Você está bloqueando a interface do usuário usando a propriedade Task.Result. Na documentação do MSDN, eles mencionaram claramente que,
A melhor solução para esse cenário seria remover os modos de espera e assíncrona e usar apenas a Tarefa em que você está retornando o resultado. Não vai estragar sua sequência de execução.
fonte
Se você não receber retornos de chamada ou o controle desligar, depois de chamar a função de serviço / API assíncrona, será necessário configurar o Contexto para retornar um resultado no mesmo contexto chamado.
Usar
TestAsync().ConfigureAwait(continueOnCapturedContext: false);
Você enfrentará esse problema apenas em aplicativos da Web, mas não em
static void main
.fonte
ConfigureAwait
evita o impasse em determinados cenários por não ser executado no contexto do encadeamento original.