У 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.
Другие вещи, которые я пробовал:
Я попытался изменить вторую последнюю строку в соответствии с приведенными здесь предложениями: Reactive Extensions Rx - модульное тестирование чего-либо с помощью ObserveOnDispatcher Никакой любви.
Я добавил
.Take(1)
в код Rx следующим образом:func (inData) .ObserveOnDispatcher (). Take (1) .Subscribe (x => Foo (x));
Это увеличило количество моих неудач примерно до 1 из 5, но они все равно имели место.
- Я переписал код Rx, чтобы использовать простой шаблон Jane Async. Это работает, однако мое эго разработчика действительно хотело бы использовать Rx вместо утомительного старого begin / end.
В конце концов, у меня есть работа (т.е. не использую Rx), но я считаю, что это не идеально. Если кто-то сталкивался с этой проблемой в прошлом и нашел решение, я бы очень хотел его услышать.
Обновление:
Я также разместил на Rx форумы, и они будут включать планировщик тестирования в ближайший выпуск. Это, вероятно, будет окончательным решением, когда оно станет доступным.