4 votes

Problème lors du test unitaire d'un code multi-filière

J'ai mis en place un Pipe qui utilise en interne une classe BlockingQueue pour stocker les données qu'il reçoit.

Il existe deux situations dans lesquelles le BlockingQueue bloquera le thread appelant :

  1. Le thread appelant fait un appel à Dequeue() et la file d'attente est vide. Il bloquera le fil jusqu'à ce qu'il y ait un élément à récupérer.
  2. Le thread appelant fait un appel à Enqueue() et la file d'attente est pleine. Il bloquera le thread jusqu'à ce qu'il y ait à nouveau de la place pour insérer les données.

Mon idée initiale était qu'au lieu d'avoir le Pipe instancie la classe BlockingQueue je passerais une instance d'un IQueue par injection de constructeur. De cette façon, lors des tests, je lui passerais une instance d'un fichier NonBlockingQueue Ainsi, je n'aurais pas à me préoccuper des problèmes de threading (lorsque je fais des tests unitaires, tant pour l'interface utilisateur que pour l'interface utilisateur). Pipe et pour les autres classes qui utilisent Pipes J'aimerais simplement ajouter des choses aux files d'attente et ne pas avoir à me demander si elles sont déjà pleines et d'autres choses du genre).

Le problème, c'est qu'en faisant ça, je fais en fait de mon Pipe se comportent de 2 manières totalement différentes, selon le type de IQueue instance à laquelle je passe :

  • Dans un BlockingQueue si la file d'attente est vide et que vous essayez de récupérer quelque chose, elle va bloquer jusqu'à ce que jusqu'à ce qu'il obtienne quelque chose. Dans une NonBlockingQueue il va juste vomir une exception.

  • Dans un BlockingQueue Si la file d'attente est pleine et que vous essayez d'ajouter quelque chose, elle attendra que quelqu'un retire un élément de la file et qu'il y ait à nouveau de la place. A NonBlockingQueue va soit faire apparaître un FullQueueException ou permettra un nombre "infini" d'éléments.

C'est-à-dire qu'il n'y a pas de "contrat unique". Je pense que cette approche est totalement erronée.

Quelle est l'approche la plus appropriée à cet égard ?

Modifier pour Mitch :

Ceci est utilisé pour implémenter un système Pipe&Filter : chaque filtre a un tuyau d'entrée et un tuyau de sortie. Chaque filtre est alors implémenté avec du code de la forme

char c;
while ((c = inputPipe.ReadChar()) != STREAM_TERMINATOR) { 
//I don't have to care 
//if right now there is any data. I know that if there isn't, 
//the thread will block and this will continue after there is some.

    ...do processing
    outputPipe.WriteChar(something);
}
outputPipe.WriteChar(STREAM_TERMINATOR);

donc je suppose que oui, avoir des tuyaux / files d'attente bloquants est le comportement que je veux.

1voto

JBSnorro Points 802

L'idée des tests unitaires est que vous testez de petites portions de code, et qu'avec un grand nombre de ces tests, vous finissez par tester tout votre code. Il y a une raison pour laquelle il est décomposé en morceaux, tester un morceau à la fois est plus facile que de tout tester simultanément. Dans le code, vous utilisez la BlockingQueue, et dans les tests, vous utilisez une NonBlockingQueue, ce qui introduit toutes sortes d'aspects difficiles et va à l'encontre du but des tests unitaires... Dans les tests, il faut simplifier, pas compliquer. Alors pourquoi ne pas utiliser une BlockingQueue là aussi ? Vous dites que le threading pourrait être un problème, mais utilisez au moins une implémentation simple de IQueue qui, de l'extérieur, fonctionne exactement comme un BlockingQueue, mais sans les problèmes de threading. Dans les tests unitaires, vous devriez être en mesure de fournir une instance de IQueue qui ne lève pas d'exceptions. Par exemple, au lieu de lever une exception lorsque la file d'attente est vide, il suffit de créer un nouvel élément. Cela est autorisé dans les tests....

1voto

P.K Points 4848

Votre idée d'avoir une implémentation injectée par le constructeur de l'application IQueue est correct.

Maintenant, qu'est-ce que tu veux tester en unité ici ?

  1. IQueue mise en œuvre
  2. Pipe responsabilité (en dehors de l'interaction avec IQueue mise en œuvre)
  3. Interaction de Pipe y IQueue mise en œuvre

Vous voulez vous concentrer sur le point n°3. Je pense que vous devez penser aux responsabilités ici. A qui incombe la responsabilité de s'assurer qu'une file d'attente est bloquée ou non. C'est la responsabilité d'un IQueue et non pas Pipe . Donc, IQueue aura simplement une "action" (un appel de méthode) qui se produit lorsque nous avons une situation exceptionnelle (sortie de la file d'attente vide ou ajout à une file d'attente pleine). Vous voulez tester cette interaction à l'unité, ce qui signifie simplement tester si la méthode d'action est appelée dans la situation exceptionnelle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X