2012-08-30 16 views
31

Tengo una aplicación ASP.NET Web API, con un ApiController que presenta métodos asíncronos, devuelve Task<> objetos y marcado con la palabra clave async.¿Cómo pruebo un método asíncrono con NUnit (o posiblemente con otro marco)?

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

¿Cómo puedo escribir las pruebas NUnit para los métodos asíncronos del ApiController? Si necesito usar otro marco de prueba, estoy abierto para eso también. Soy bastante nuevo en pruebas de unidad .NET en general, por lo que estoy interesado en aprender las mejores prácticas.

+8

Esto ahora es compatible con la versión 2.6.2 – Simone

+0

@Simone ¡Eso fue más rápido de lo esperado! – aknuds1

Respuesta

13

Me parece que no hay compatibilidad integrada en NUnit 2.6 para probar métodos asíncronos que devuelven tareas. La mejor opción que puedo ver ahora es usar el framework UnitTesting de Visual Studio o xUnit.net as both support asynchronous tests.

Con el marco de Visual Studio prueba unitaria que puedo escribir pruebas asíncronos como esto:

using Microsoft.VisualStudio.TestTools.UnitTesting; 

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

Una buena noticia es compatible con [NUnit 2.6.2] (http://nunit.org/?p=releaseNotes&r=2.6.2), y en [Resharper 7.1] (http://youtrack.jetbrains.com/issue/ RSRP-332732) – andreister

2

Realmente depende de lo que están haciendo. Por lo general, dependerán de algo else que proporciona un Task<T> o algo similar. En ese caso, es posible que pueda proporcionar dependencias falsas que le permitan controlar todo esto de manera detallada. Tengo un prototipo de "máquina del tiempo" que le permite preprogramar tareas para completar en tiempos artificiales particulares, luego avanzar un tiempo y realizar afirmaciones sobre la marcha. Hay un blog post about it que puede serle útil. Como ya dije, es solo un prototipo y no es apropiado para todos los escenarios de, pero puede ser adecuado para usted.

Stephen Cleary también tiene un par de publicaciones en el blog sobre pruebas unitarias (1, 2), teniendo un enfoque ligeramente diferente, junto con un NuGet package que puede serle útil.

El enfoque básico es el mismo que el normal: dale a tu método diferentes entradas (y salidas de dependencia) y prueba los resultados. Definitivamente es más difícil lograr eso con la asincronía, pero es factible.

+0

Analizará sus sugerencias, gracias. He encontrado al menos que 'Microsoft.VisualStudio.TestTools.UnitTesting' tiene soporte directo para pruebas asíncronas (tareas que vuelven). – aknuds1

2

estoy en el proceso de conversión de algunos de mis métodos para async. Hacer que esto funcione con NUnit ha sido bastante sencillo.

Los métodos de prueba no pueden ser asincrónicos. Pero todavía tenemos acceso a la funcionalidad completa de la Biblioteca de tareas paralelas, simplemente no podemos usar la palabra clave await directamente en el método de prueba.

En mi ejemplo, yo tenía un método:

public string SendUpdateRequestToPlayer(long playerId) 

Y se puso a prueba en nUnit así:

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

Ahora que he alterado el método SendUpdateRequestToPlayer ser un asíncrono

 public async Task<string> SendUpdateRequestToPlayer(long playerId) 

simplemente tenía que modificar mis pruebas para Wait f o la tarea para completar:

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(); 
+0

¡Esto funciona bien, gracias! Actualmente no tenemos más opción que utilizar NUnit, así que esto nos ha ayudado mucho. Estamos ejecutando las pruebas winRT/.net 4.5 escritas con vs 2012 en Windows 8 y ejecutando las pruebas con un agente TFS 2010 que se ejecuta en Windows 8. Solo una actividad NUnit de flujo de trabajo de TFS personalizada nos permitió ejecutar la prueba. – ehuna

+0

No creo que deba usar Wait así en un caso de prueba. Para empezar, Wait está bloqueando, lo que creo que sabes. Pero supongo que eso podría hacer que tu prueba sea bastante larga. Además, dado que Task ajustará excepciones en AggregateException, podría estar probando algo que quizás nunca ocurra. Stephen Cleary (como ha dicho otro) tiene 2 buenos blogs sobre esto: http://blog.stephencleary.com/2012/02/async-unit-tests-part-1-wrong-way.html – sacha

+1

@sacha - I don No entiendo su argumento de "bloqueo": si desea que sus pruebas se ejecuten secuencialmente, por supuesto debe esperar a que se complete una prueba antes de ejecutar el código de limpieza y luego iniciar el siguiente. Cualquier prueba de código multiproceso implicará el bloqueo en ALGUNA PARTE - este ejemplo simplemente lo hace obvio donde está sucediendo. El segundo punto es obtener una "AggregateException" en lugar de la excepción lanzada real. Prácticamente todas mis pruebas están probando que no se lanza ninguna excepción. Cuando una prueba arroja una excepción, siempre uso el depurador para descubrir qué está pasando. –

6

tener en cuenta que la serie NUnit 2.6, igual que los anteriores, se construye para apuntar el marco .NET 2.0.Por lo tanto, NUnit solo puede hacer las mismas cosas que podría codificar usted mismo, en un ensamblado que tenga como objetivo .NET 2.0.

En concreto, no se puede marcar su método de prueba como asíncrono y esperan NUnit que hacer nada especial con ella.

Puede, sin embargo,

  • destino .NET 4.5 para las pruebas. NUnit los ejecutará en un proceso separado .
  • Uso esperan en su prueba para esperar el resultado de una llamada a un método asíncrono.

Ninguna de las anteriores le dará la ejecución de pruebas asíncrono, si eso es lo que está esperando. No se ejecutarán otras pruebas mientras se espera que se complete la operación asincrónica.

Otra opción es usar NUnitLite. NUnitLite 0.8 admite el atributo [Asynchronous] que permitirá que otras pruebas continúen mientras se completa la prueba asincrónica. La ventaja del atributo es que permite pruebas asíncronos para trabajar en .NET 2.0 a través de 4.0

estos momentos, no tenemos una acumulación de .NET 4.5 NUnitLite pero se añadirán en breve y estamos trabajando en un cambio que hará uso de [Asincrónico] opcional en ese entorno. Por ahora, puede descargar y volver a compilar fácilmente el código fuente de .NET 4.5.

Para el futuro, busque NUnit 3.0 para que sea compatible con los métodos asíncronos totalmente junto con la ejecución paralela general de pruebas en varios subprocesos o en procesos múltiples.

+0

Hola Charlie, ¿hay alguna noticia sobre ese NUnit 3? :) – Noctis

32

A partir de hoy (07/02/2014) prueba asíncrono es apoyado por:

En el primer dos marcos, el método de prueba debe tener esta firma:

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

NUnit, aparte de que la firma, es compatible con éste:

public async void MyTestMethod() 

Por supuesto, dentro de cualquiera de estos métodos de ensayo se puede utilizar await que llamar y esperar en métodos asíncronos.

Si está utilizando un marco de prueba que no admite métodos de prueba asincrónicos, entonces, la única forma de hacerlo es llamar al método asíncrono y esperar hasta que termine de ejecutarse utilizando cualquiera de las formas habituales: await , leyendo la propiedad Result del Task<T> devuelto por un método async, utilizando cualquiera de los métodos de espera habituales de Task y así sucesivamente. Después de la espera, puede hacer todas las afirmaciones como de costumbre. Por ejemplo, usando 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); 
    ... 
} 
Cuestiones relacionadas