Les Extensions Réactives ont un petit crochet sexy pour simplifier l'appel des méthodes asynchrones :
var func = Observable.FromAsyncPattern(
myWcfService.BeginDoStuff,
myWcfService.EndDoStuff);
func(inData).ObserveOnDispatcher().Subscribe(x => Foo(x));
Je l'utilise dans un projet WPF, et cela fonctionne très bien à l'exécution.
Malheureusement, lors de la tentative de tester unitairement les méthodes utilisant cette technique, je rencontre des échecs aléatoires. Environ 3 tests sur cinq contenant ce code échouent.
Voici un exemple de test (implémenté en utilisant un conteneur de mockage automatique Rhino/Unity) :
[TestMethod()]
public void SomeTest()
{
// arrange
var container = GetAutoMockingContainer();
container.Resolve()
.Expect(x => x.BeginDoStuff(null, null, null))
.IgnoreArguments()
.Do(
new Func((inData, asyncCallback, state) =>
{
return new CompletedAsyncResult(asyncCallback, state);
}));
container.Resolve()
.Expect(x => x.EndDoStuff(null))
.IgnoreArguments()
.Do(
new Func((ar) =>
{
return someMockData;
}));
// act
var target = CreateTestSubject(container);
target.DoMethodThatInvokesService();
// Exécuter le dispatcher pour tout à la priorité de fond
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, new Action(() => { }));
// assert
Assert.IsTrue(mon opération s'est bien déroulée);
}
Le problème que je vois est que le code spécifié pour s'exécuter lorsque l'action asynchrone est terminée (dans ce cas, Foo(x)), n'est jamais appelé. Je peux le vérifier en plaçant des points d'arrêt dans Foo et en observant qu'ils ne sont jamais atteints. De plus, je peux forcer un long délai après avoir appelé DoMethodThatInvokesService (qui lance l'appel asynchrone), et le code n'est toujours pas exécuté. Je suis sûr que les lignes de code invoquant le framework Rx ont été appelées.
Autres choses que j'ai essayées :
-
J'ai tenté de modifier l'avant-dernière ligne selon les suggestions ici : Extensions Réactives Rx - test unitaire de quelque chose avec ObserveOnDispatcher. Aucun succès.
-
J'ai ajouté
.Take(1)
au code Rx comme suit :func(inData).ObserveOnDispatcher().Take(1).Subscribe(x => Foo(x));
Cela a amélioré mon taux d'échec à environ 1 sur 5, mais les échecs sont toujours survenus.
- J'ai réécrit le code Rx pour utiliser le modèle asynchrone classique. Cela fonctionne, cependant mon ego de développeur aimerait vraiment utiliser Rx au lieu de l'ancien modèle begin/end.
En fin de compte, j'ai une solution de contournement (c'est-à-dire ne pas utiliser Rx), cependant je sens que ce n'est pas idéal. Si quelqu'un a rencontré ce problème dans le passé et a trouvé une solution, j'aimerais vraiment l'entendre.
Mise à jour :
J'ai également publié sur les forums Rx, et ils incluront un planificateur de test dans une prochaine version. Ce sera probablement la solution ultime une fois disponible.