Tengo las siguientes cuatro pruebas y la última se cuelga cuando la ejecuto. Por qué pasó esto:
[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;
}
Utilizo este método de extensión para 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 qué se cuelga la última prueba?
async void
métodos de prueba de unidad en absoluto; simplemente no funcionarán. Sin embargo, NUnit sí. Dicho esto, estoy de acuerdo con el principio general de preferirasync Task
sobreasync void
.Respuestas:
Se encuentra con la situación de bloqueo estándar que describo en mi blog y en un artículo de MSDN : el
async
método está intentando programar su continuación en un hilo que está bloqueado por la llamada aResult
.En este caso,
SynchronizationContext
NUnit utiliza el suyo para ejecutarasync void
métodos de prueba. Intentaría usarasync Task
métodos de prueba en su lugar.fuente
async
todo el camino" (como se señala en mi artículo de MSDN). En otras palabras, como dice el título de mi blog, "no bloquee el código asíncrono".Wait()
con hacer el método de llamadaasync
. Pero para mí, esto parece estar empujando el problema hacia arriba. En algún momento, algo tiene que ser administrado sincrónicamente. ¿Qué sucede si mi función es intencionadamente sincrónica porque administra hilos de trabajo de larga ejecuciónTask.Run()
? ¿Cómo espero a que termine sin bloquearse dentro de mi prueba NUnit?At some point, something has to be managed synchronously.
- en absoluto. Para aplicaciones de IU, el punto de entrada puede ser unasync void
controlador de eventos. Para aplicaciones de servidor, el punto de entrada puede ser unaasync Task<T>
acción. Es preferible usarasync
ambos para evitar el bloqueo de hilos. Puede hacer que su prueba de NUnit sea síncrona o asíncrona; si es asíncrono, hágalo enasync Task
lugar deasync void
. Si es sincrónico, no debería tener un,SynchronizationContext
así que no debería haber un punto muerto.Adquisición de un valor a través de un método asíncrono:
Llamar sincrónicamente un método asíncrono
No se producirán problemas de punto muerto debido al uso de Task.Run.
fuente
async void
métodos de prueba unitaria y eliminar las garantías del mismo hilo proporcionadas porSynchronizationContext
el sistema bajo prueba..GetAwaiter().GetResult()
lugar de hacerlo.Result
para que ninguno quedeException
envuelto.Puede evitar el punto muerto que se agrega
ConfigureAwait(false)
a esta línea:=>
Describí este escollo en la publicación de mi blog Escollos de async / await
fuente
Está bloqueando la IU utilizando la propiedad Task.Result. En la documentación de MSDN , han mencionado claramente que,
La mejor solución para este escenario sería eliminar los modos de espera y asíncrono de los métodos y usar solo la Tarea donde devuelve el resultado. No alterará su secuencia de ejecución.
fuente
Si no recibe ninguna devolución de llamada o el control se cuelga, después de llamar a la función asíncrona de servicio / API, debe configurar Context para devolver un resultado en el mismo contexto llamado.
Utilizar
TestAsync().ConfigureAwait(continueOnCapturedContext: false);
Se enfrentará a este problema solo en aplicaciones web, pero no en
static void main
.fuente
ConfigureAwait
evita el interbloqueo en ciertos escenarios al no ejecutarse en el contexto del hilo original