9
votes

Tests unitaires avec des simulacres lorsque la solution est en train de tirer parti de la bibliothèque parallèle de tâche

J'essaie d'appuyer sur un appareil / vérifie qu'une méthode est appelée sur une dépendance, par le système sous Test (SUT).

  • la dépendance est ifoo.
  • La classe dépendante est ibar.
  • ibar est implémenté comme barre.
  • Barre appellera Démarrer () sur IFOO dans une tâche nouvelle (System.Threading.Tasks.) Lorsque START () est appelé l'instance de barre.

    test d'unité (MOQ): xxx

    implémentation de ibar comme barre: xxx

    MOQ Exception: xxx


1 commentaires

Y a-t-il une raison particulière de ne pas écrire une simple implémentation simulée de ifoo vous-même et utilisez-le à la place?


3 Réponses :


0
votes

Vos tests utilisent trop de détails de mise en œuvre, iEnumerable types. Chaque fois que je dois commencer à tester avec iEnumerable, il crée toujours des frictions.


0 commentaires

7
votes

@DPurrington & @stevenh: Si nous commençons à mettre ce genre de choses dans notre code xxx

et nous avons des tests "unités", puis nos tests commencent à courir dans les minutes au lieu de secondes. Si vous aviez par exemple des tests unitaires, il sera difficile de faire fonctionner vos tests de moins de 5 secondes si quelqu'un est parti et a monté la base de code de test avec fil.sleep.

Je suggère que c'est Mauvaise pratique, sauf si nous faisons explicitement des tests d'intégration.

Ma suggestion serait d'utiliser l'interface System.CONCurrency.Schéduleur de System.CoreEx.dll et injectez l'implémentation TaskpoolSchéduler.

C'est ma suggestion de la manière dont cela devrait être implémenté xxx

ceci permet maintenant au test de fonctionner à pleine vitesse sans aucun thread.sleep.

Notez que les contrats ont été modifiés pour accepter un ISCHEDuler dans le constructeur de barres (pour l'injection de dépendance) et que l'iNumérable est maintenant transmis à la méthode iBar.Start. J'espère que cela a du sens pourquoi j'ai apporté ces changements.

La vitesse des tests est le premier et le plus évident que de le faire. Le second et éventuellement un avantage plus important de faire cela est lorsque vous introduisez une simultanéité plus complexe à votre code, ce qui rend les tests notoirement difficiles. L'interface ISCHEDuler et le testScheduler peuvent vous permettre d'exécuter des "tests unitaires" déterministes même de la simultanéité plus complexe.


4 commentaires

Je suis d'accord avec votre point initial sur ma solution. Je pense que j'aurais choisi d'utiliser des événements au lieu d'un planificateur, mais de toute façon meilleure que ma réponse.


Malheureusement, cela ne fonctionne plus, comme toutes les méthodes de .Threading.tasks.taskschéduler sont scellés.


@Richard: Notez que l'injection d'injection est l'injection d'interface que le testchédulgleur implémente. Je n'essaie pas de remplacer les méthodes sur le Tasksheduler, je tire parti du fait que les Taskscheduler et TestScheduler mettent en œuvre l'interface ISCHEDuler et, en tant que telles, sont interchangeables.


Ahh Désolé @Richard, je vois que nous parlons de 2 types différents nommés la même chose. Je parle de System.Reactive.ConCurrency.Schéduleur et le System.Reactive.ConCurrency.schéduler.Taskpool qui expose le type System.Reactive.ConCurrency.takpoolschéduler. C'est donc tout dans le cadre d'extension réactif. C'est génial, vérifiez-le.



0
votes

thread.sleep () est définitivement une mauvaise idée. J'ai lu ainsi plusieurs fois sur cette "vraie applications" ne dormez pas ". Prenez cela comme vous, mais je suis d'accord avec cette déclaration. Surtout lors des tests d'unité. Si votre code de test crée de fausses échecs, vos tests sont fragiles.

J'ai récemment écrit des tests qui attendent correctement les tâches parallèles pour terminer l'exécution de l'exécution et je pensais partager ma solution. Je me rends compte que c'est un ancien poste, mais je pensais que cela donnerait de la valeur à ceux qui recherchent une solution. P>

Ma mise en œuvre consiste à modifier la classe sous test et méthode sous test. P>

[Test]
public void StartBar_ShouldCallStartOnAllFoo_WhenFoosExist()
{
    //ARRANGE

    var mockFoo0 = new Mock<IFoo>();
    mockFoo0.Setup(foo => foo.Start());

    var mockFoo1 = new Mock<IFoo>();
    mockFoo1.Setup(foo => foo.Start());


    //Add mockobjects to a collection
    var foos = new List<IFoo> { mockFoo0.Object, mockFoo1.Object };

    IBar sutBar = new Bar(foos);

    //ACT
    sutBar.Start(); //Should call mockFoo.Start()
    sutBar.FooCountdown.Wait(); // this blocks until all parallel tasks in sutBar complete

    //ASSERT
    mockFoo0.VerifyAll();
    mockFoo1.VerifyAll();
}


0 commentaires