Ok, no estoy seguro si lo siguiente será de alguna ayuda para usted, porque hice algunas suposiciones en el desarrollo de una solución que puede o no ser cierta en su caso. Tal vez mi "solución" es demasiado teórica y solo funciona para ejemplos artificiales: no he hecho ninguna prueba más allá de las siguientes.
Además, vería lo siguiente más una solución alternativa que una solución real, pero teniendo en cuenta la falta de respuestas, creo que aún podría ser mejor que nada (seguí mirando su pregunta esperando una solución, pero al no ver una publicación, comencé a jugar alrededor con el problema).
Pero suficiente dijo: Digamos que tenemos un servicio de datos simple que se puede utilizar para recuperar un número entero:
public interface IDataService
{
Task<int> LoadMagicInteger();
}
Una implementación simple usa código asincrónico:
public sealed class CustomDataService
: IDataService
{
public async Task<int> LoadMagicInteger()
{
Console.WriteLine("LoadMagicInteger - 1");
await Task.Delay(100);
Console.WriteLine("LoadMagicInteger - 2");
var result = 42;
Console.WriteLine("LoadMagicInteger - 3");
await Task.Delay(100);
Console.WriteLine("LoadMagicInteger - 4");
return result;
}
}
Ahora, surge un problema, si estamos usando el código "incorrectamente" como se ilustra en esta clase. Foo
accede incorrectamente en Task.Result
lugar de obtener await
el resultado como lo Bar
hace:
public sealed class ClassToTest
{
private readonly IDataService _dataService;
public ClassToTest(IDataService dataService)
{
this._dataService = dataService;
}
public async Task<int> Foo()
{
var result = this._dataService.LoadMagicInteger().Result;
return result;
}
public async Task<int> Bar()
{
var result = await this._dataService.LoadMagicInteger();
return result;
}
}
Lo que necesitamos (usted) ahora es una forma de escribir una prueba que tenga éxito al llamar Bar
pero falla al llamar Foo
(al menos si entendí la pregunta correctamente ;-)).
Dejaré hablar el código; esto es lo que se me ocurrió (usando las pruebas de Visual Studio, pero también debería funcionar usando NUnit):
DataServiceMock
utiliza TaskCompletionSource<T>
. Esto nos permite establecer el resultado en un punto definido en la ejecución de la prueba que conduce a la siguiente prueba. Tenga en cuenta que estamos utilizando un delegado para devolver el TaskCompletionSource a la prueba. También puede poner esto en el método Initialize de la prueba y usar las propiedades.
TaskCompletionSource<int> tcs = null;
this._dataService.LoadMagicIntegerMock = t => tcs = t;
Task<int> task = null;
TaskTestHelper.AssertDoesNotBlock(() => task = this._instance.Foo());
tcs.TrySetResult(42);
var result = task.Result;
Assert.AreEqual(42, result);
this._end = true;
Lo que sucede aquí es que primero verificamos que podemos dejar el método sin bloquear (esto no funcionaría si alguien accediera Task.Result
; en este caso, tendríamos un tiempo de espera ya que el resultado de la tarea no está disponible hasta después de que el método haya regresado )
Luego, establecemos el resultado (ahora el método puede ejecutarse) y verificamos el resultado (dentro de una prueba unitaria podemos acceder a Task.Result ya que realmente queremos que ocurra el bloqueo).
Clase de prueba completa: BarTest
tiene éxito y FooTest
falla según lo deseado.
[TestClass]
public class UnitTest1
{
private DataServiceMock _dataService;
private ClassToTest _instance;
private bool _end;
[TestInitialize]
public void Initialize()
{
this._dataService = new DataServiceMock();
this._instance = new ClassToTest(this._dataService);
this._end = false;
}
[TestCleanup]
public void Cleanup()
{
Assert.IsTrue(this._end);
}
[TestMethod]
public void FooTest()
{
TaskCompletionSource<int> tcs = null;
this._dataService.LoadMagicIntegerMock = t => tcs = t;
Task<int> task = null;
TaskTestHelper.AssertDoesNotBlock(() => task = this._instance.Foo());
tcs.TrySetResult(42);
var result = task.Result;
Assert.AreEqual(42, result);
this._end = true;
}
[TestMethod]
public void BarTest()
{
TaskCompletionSource<int> tcs = null;
this._dataService.LoadMagicIntegerMock = t => tcs = t;
Task<int> task = null;
TaskTestHelper.AssertDoesNotBlock(() => task = this._instance.Bar());
tcs.TrySetResult(42);
var result = task.Result;
Assert.AreEqual(42, result);
this._end = true;
}
}
Y una pequeña clase de ayuda para probar los puntos muertos / tiempos de espera:
public static class TaskTestHelper
{
public static void AssertDoesNotBlock(Action action, int timeout = 1000)
{
var timeoutTask = Task.Delay(timeout);
var task = Task.Factory.StartNew(action);
Task.WaitAny(timeoutTask, task);
Assert.IsTrue(task.IsCompleted);
}
}
async
artículos de este chico ?