unitarias pruebas operador example await async c# .net unit-testing async-await

pruebas - operador await c#



Esperar una llamada al método Async Void para pruebas unitarias (6)

Deberías evitar el async void . Solo use el async void para los manejadores de eventos. DelegateCommand es (lógicamente) un controlador de eventos, por lo que puede hacerlo así:

// Use [InternalsVisibleTo] to share internal methods with the unit test project. internal async Task DoLookupCommandImpl(long idToLookUp) { IOrder order = await orderService.LookUpIdAsync(idToLookUp); // Close the search IsSearchShowing = false; } private async void DoStuff(long idToLookUp) { await DoLookupCommandImpl(idToLookup); }

y la unidad lo prueba como:

[TestMethod] public async Task TestDoStuff() { //+ Arrange myViewModel.IsSearchShowing = true; // container is my Unity container and it setup in the init method. container.Resolve<IOrderService>().Returns(orderService); orderService = Substitute.For<IOrderService>(); orderService.LookUpIdAsync(Arg.Any<long>()) .Returns(new Task<IOrder>(() => null)); //+ Act await myViewModel.DoLookupCommandImpl(0); //+ Assert myViewModel.IsSearchShowing.Should().BeFalse(); }

Mi respuesta recomendada está arriba. Pero si realmente quieres probar un método de async void , puedes hacerlo con mi biblioteca AsyncEx :

[TestMethod] public void TestDoStuff() { AsyncContext.Run(() => { //+ Arrange myViewModel.IsSearchShowing = true; // container is my Unity container and it setup in the init method. container.Resolve<IOrderService>().Returns(orderService); orderService = Substitute.For<IOrderService>(); orderService.LookUpIdAsync(Arg.Any<long>()) .Returns(new Task<IOrder>(() => null)); //+ Act myViewModel.DoLookupCommand.Execute(0); }); //+ Assert myViewModel.IsSearchShowing.Should().BeFalse(); }

Pero esta solución cambia el SynchronizationContext para su modelo de vista durante su vida útil.

Tengo un método que se ve así:

private async void DoStuff(long idToLookUp) { IOrder order = await orderService.LookUpIdAsync(idToLookUp); // Close the search IsSearchShowing = false; } //Other stuff in case you want to see it public DelegateCommand<long> DoLookupCommand{ get; set; } ViewModel() { DoLookupCommand= new DelegateCommand<long>(DoStuff); }

Estoy tratando de probarlo en una unidad así:

[TestMethod] public void TestDoStuff() { //+ Arrange myViewModel.IsSearchShowing = true; // container is my Unity container and it setup in the init method. container.Resolve<IOrderService>().Returns(orderService); orderService = Substitute.For<IOrderService>(); orderService.LookUpIdAsync(Arg.Any<long>()) .Returns(new Task<IOrder>(() => null)); //+ Act myViewModel.DoLookupCommand.Execute(0); //+ Assert myViewModel.IsSearchShowing.Should().BeFalse(); }

Mi afirmación es llamada antes de que termine con el lookUpIdAsync engañado. En mi código normal, eso es justo lo que quiero. Pero para mi prueba de unidad no quiero eso.

Estoy convirtiendo a Async / Await de usar BackgroundWorker. Con el trabajador de fondo, esto funcionaba correctamente porque podía esperar a que BackgroundWorker terminara.

Pero no parece haber una manera de esperar un método de vacío asíncrono ...

¿Cómo puedo probar la unidad de este método?


Descubrí una forma de hacerlo para pruebas unitarias:

[TestMethod] public void TestDoStuff() { //+ Arrange myViewModel.IsSearchShowing = true; // container is my Unity container and it setup in the init method. container.Resolve<IOrderService>().Returns(orderService); orderService = Substitute.For<IOrderService>(); var lookupTask = Task<IOrder>.Factory.StartNew(() => { return new Order(); }); orderService.LookUpIdAsync(Arg.Any<long>()).Returns(lookupTask); //+ Act myViewModel.DoLookupCommand.Execute(0); lookupTask.Wait(); //+ Assert myViewModel.IsSearchShowing.Should().BeFalse(); }

La clave aquí es que, debido a que estoy probando unidades, puedo sustituir en la tarea que quiero que mi llamada asincrónica (dentro de mi vacío asíncrono) regrese. Luego solo me aseguro de que la tarea se haya completado antes de continuar.


La única forma que conozco es convertir tu método "aync void" en el método "async Task"


La respuesta proporcionada prueba el comando y no el método async. Como se mencionó anteriormente, necesitará otra prueba para probar ese método asíncrono también.

Después de pasar algo de tiempo con un problema similar, encontré una espera fácil para probar un método asíncrono en una prueba unitaria simplemente llamando de forma sincrónica:

protected static void CallSync(Action target) { var task = new Task(target); task.RunSynchronously(); }

y el uso:

CallSync(() => myClass.MyAsyncMethod());

La prueba espera en esta línea y continúa después de que el resultado esté listo para que podamos afirmar inmediatamente después.


Puede usar un AutoResetEvent para detener el método de prueba hasta que se complete la llamada asincrónica:

[TestMethod()] public void Async_Test() { TypeToTest target = new TypeToTest(); AutoResetEvent AsyncCallComplete = new AutoResetEvent(false); SuccessResponse SuccessResult = null; Exception FailureResult = null; target.AsyncMethodToTest( (SuccessResponse response) => { SuccessResult = response; AsyncCallComplete.Set(); }, (Exception ex) => { FailureResult = ex; AsyncCallComplete.Set(); } ); // Wait until either async results signal completion. AsyncCallComplete.WaitOne(); Assert.AreEqual(null, FailureResult); }


Un método de async void es esencialmente un método de "fuego y olvídate". No hay forma de recuperar un evento de finalización (sin un evento externo, etc.).

Si necesita probar esta unidad, le recomiendo que sea un método de async Task . Luego puede llamar a Wait() en los resultados, que le notificarán cuando el método finalice.

Sin embargo, este método de prueba tal como está escrito aún no funcionaría, ya que en realidad no está probando DoStuff directamente, sino que está probando un DelegateCommand que lo envuelve. Debería probar este método directamente.