Reactive Extensions には、非同期メソッドの呼び出しを簡素化するための魅力的な小さなフックがあります。
var func = Observable.FromAsyncPattern<InType, OutType>(
myWcfService.BeginDoStuff,
myWcfService.EndDoStuff);
func(inData).ObserveOnDispatcher().Subscribe(x => Foo(x));
これを WPF プロジェクトで使用していますが、実行時にうまく機能します。
残念ながら、この手法を使用するメソッドを単体テストしようとすると、ランダムなエラーが発生します。このコードを含むテストの 5 回の実行ごとに ~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 フレームワークを呼び出すコード行が呼び出されたことは知っています。
私が試した他のこと:
ここでの提案に従って、最後から 2 番目の行を変更しようとしまし た 。
.Take(1)
次のように Rx コードに追加しました。func(inData).ObserveOnDispatcher().Take(1).Subscribe(x => Foo(x));
これにより、失敗率が 5 分の 1 程度に改善されましたが、それでも発生しました。
- 単純なジェーン非同期パターンを使用するように Rx コードを書き直しました。これは機能しますが、私の開発者のエゴは、退屈な古い開始/終了の代わりに Rx を使用したいと思っています。
最終的には回避策 (つまり、Rx を使用しない) がありますが、それは理想的ではないと感じています。誰かが過去にこの問題に遭遇し、解決策を見つけた場合は、ぜひ聞いてください.
更新:
Rx フォーラムにも投稿しましたが、今後のリリースにはテスト スケジューラが含まれる予定です。それが利用可能になったら、おそらくそれが究極の解決策になるでしょう。