Есть ли способ модульного тестирования асинхронного метода?
Я использую Xunit и NMock на платформе.NET. Я тестирую модель презентации, где метод является асинхронным. Метод создает асинхронную задачу и выполняет ее, поэтому метод немедленно возвращается, и состояние, которое мне нужно проверить, еще не готово.
Я могу установить флаг по окончании без изменения SUT, но это будет означать, что мне придется постоянно проверять флаг, например, в цикле while, возможно с таймаутом.
Какие у меня варианты?
5 ответов
Имеет ли ваш объект какой-либо сигнал о завершении асинхронного метода, например, событие? Если это так, вы можете использовать следующий подход:
[Test]
public void CanTestAsync()
{
MyObject instance = new MyObject()
AutoResetEvent waitHandle = new AutoResetEvent(false);
// create and attach event handler for the "Finished" event
EventHandler eventHandler = delegate(object sender, EventArgs e)
{
waitHandle.Set(); // signal that the finished event was raised
}
instance.AsyncMethodFinished += eventHandler;
// call the async method
instance.CallAsyncMethod();
// Wait until the event handler is invoked
if (!waitHandle.WaitOne(5000, false))
{
Assert.Fail("Test timed out.");
}
instance.AsyncMethodFinished -= eventHandler;
Assert.AreEqual("expected", instance.ValueToCheck);
}
Просто подумал, что вам может понадобиться обновление по этому вопросу, поскольку ответ № 1 на самом деле рекомендует более старый шаблон для решения этой проблемы.
В.net 4.5 + xUnit 1.9 или выше вы можете просто вернуть задачу и при желании использовать ключевое слово async из теста, чтобы xunit ожидал асинхронного завершения теста.
Смотрите эту статью на xUnit.net 1.9
[Fact]
public async Task MyAsyncUnitTest()
{
// ... setup code here ...
var result = await CallMyAsyncApi(...);
// ... assertions here ...
}
Мой предпочтительный метод состоит в том, чтобы смоделировать и внедрить фактический механизм потоков, чтобы при тестировании он не был асинхронным. Иногда это невозможно (если многопоточность метода является частью фреймворка или не находится под вашим контролем).
Если вы не можете управлять созданием потока, то ожидаете, что поток каким-то образом завершит свою работу, либо цикл while, либо просто временное ожидание, которое должен занять поток, и провалет тест, если состояние не существует с момента его все равно слишком долго
Проверить мою статью о модульном тестировании приложений Silverlight
http://www.codeproject.com/KB/silverlight/Ag3DemoLOB.aspx
Есть пример модульного тестирования метода, который вызывает службу WCF асинхронно...
Асинхронные тесты с NSubtitute и XUnit на самом деле довольно просты:
public interface IA
{
Task<int> DoSomething();
}
вызвано:
public class IAmUnderTest
{
public async Task<int> GetInt(IA a)
{
return await a.DoSomething();
}
}
Поскольку асинхронные методы просто возвращают задачи, все, что вам нужно сделать, чтобы имитировать DoSomething() с помощью NSubstitute, — это использовать Task.FromResult(). Затем вызывающий код возвращает задачу, которую он все еще может ожидать, и возвращает целочисленный результат.
Итак, издевательство над ним с помощью NSubstitute выглядит так:
public class ATest
{
[Fact]
public void DoesSomething()
{
var dependency = Substitute.For<IA>();
dependency.DoSomething().Returns(Task.FromResult(1));
var target = new IAmUnderTest();
var id = target.GetInt(dependency).Result;
id.Should().Be(1);
}
}
Добавим еще немного, сделав тест асинхронным:
public class ATest
{
[Fact]
public async Task DoesSomethingAsync()
{
var dependency = Substitute.For<IA>();
dependency.DoSomething().Returns(Task.FromResult(1));
var target = new IAmUnderTest();
var id = await target.GetInt(dependency);
id.Should().Be(1);
}
}
Последнее предпочтительнее первого при тестировании асинхронных методов.