У Reactive Extensions есть небольшой симпатичный крючок для упрощения вызова асинхронных методов:
var func = Observable.FromAsyncPattern<InType, OutType>(
myWcfService.BeginDoStuff,
myWcfService.EndDoStuff);
func(inData).ObserveOnDispatcher().Subscribe(x => Foo(x));
Я использую это в проекте WPF, и он прекрасно работает во время выполнения.
К сожалению, при попытке использовать методы юнит-тестирования, использующие эту технику, у меня возникают случайные сбои. ~ 3 из каждых пяти выполнений теста, содержащего этот код, завершается неудачей.
Вот пример теста (реализован с использованием контейнера для автоматической имитации Rhino / unity):
[TestMethod()]
public void SomeTest()
{
// arrange
var container = GetAutoMockingContainer();
container.Resolve<IMyWcfServiceClient>()
.Expect(x => x.BeginDoStuff(null, null, null))
.IgnoreArguments()
.Do(
new Func<Specification, AsyncCallback, object, IAsyncResult>((inData, asyncCallback, state) =>
{
return new CompletedAsyncResult(asyncCallback, state);
}));
container.Resolve<IRepositoryServiceClient>()
.Expect(x => x.EndDoStuff(null))
.IgnoreArguments()
.Do(
new Func<IAsyncResult, OutData>((ar) =>
{
return someMockData;
}));
// act
var target = CreateTestSubject(container);
target.DoMethodThatInvokesService();
// Run the dispatcher for everything over background priority
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, new Action(() => { }));
// assert
Assert.IsTrue(my operation ran as expected);
}
Проблема, которую я вижу, заключается в том, что код, который я указал для запуска после завершения асинхронного действия (в данном случае, Foo (x)), никогда не вызывается. Я могу убедиться в этом, установив точки останова в Foo и заметив, что они никогда не достигаются. Кроме того, я могу вызвать длительную задержку после вызова DoMethodThatInvokesService (который запускает асинхронный вызов), и код по-прежнему никогда не запускается. Я знаю , что строки кода, вызывающие структуру Rx, были вызваны.
Другие вещи, которые я пробовал:
Это улучшило мою частоту отказов до 1 к 5, но они все же произошли.
- Я переписал код Rx, чтобы использовать шаблон Async с простым jane. Это работает, однако мое эго разработчика действительно хотело бы использовать Rx вместо скучного старого начала / конца.
В конце концов, у меня есть работа вокруг (т.е. я не использую Rx), однако я чувствую, что это не идеально. Если бы кто-то сталкивался с этой проблемой в прошлом и нашел решение, я бы очень хотел ее услышать.
Обновление
Я также разместил на форумах Rx , и они будут включать планировщик тестов в следующем выпуске. Это, вероятно, будет окончательным решением, как только оно будет доступно.