Quelqu'un a-t-il des conseils pour une méthode cohérente de test unitaire d'une application multithread? J'ai fait une application où nos "threads de travail" factices avaient un thread.sleep avec un temps spécifié par une variable de membre public. Nous l'utiliserions pour définir le temps nécessaire à un thread particulier pour terminer son travail, puis nous pourrions faire nos affirmations. Avez-vous des idées d'une meilleure façon de procéder? Des bons cadres fictifs pour .Net qui peuvent gérer cela?
Réponses
Trop de publicités?La première étape est de réaliser que, souvent, une grande partie du code que vous devez unité de test est orthogonal à l'enfilage. Cela signifie que vous devriez essayer et de casser le code qui fait le travail à partir du code qui ne l'enfilage. Une fois cela fait, vous pouvez facilement tester le code qui fait le travail normal de l'unité des essais pratiques. Mais bien sûr, vous le saviez déjà.
Le problème est alors l'un des tests de filetage côté du problème, mais au moins maintenant vous avez un point où cette filetage interfaces avec le code qui fait le travail et nous espérons que vous disposez d'une interface que vous pouvez se moquer. Maintenant que vous avez une maquette pour le code que l'enfilage code appelle je trouve que la meilleure chose à faire est d'ajouter des événements à la maquette (ce qui peut signifier que vous devez à la main le rouleau de votre maquette). Les événements vont alors être utilisé pour permettre le test de synchroniser avec et bloc de filetage code sous test.
Ainsi, par exemple, permet de dire que nous avons quelque chose de très simple, un multi-thread file d'attente des processus, des éléments de travail. Vous serait se moquer de l'élément de travail. L'interface peut inclure un Processus de () la méthode que le thread appelle pour faire le travail. Vous auriez mis deux événements là-bas. Une que la fantaisie définit le moment où les Processus de() est appelée et que la maquette attend après qu'il ait mis le premier événement. Maintenant, dans votre test, vous pouvez démarrer votre file d'attente, post un simulacre d'élément de travail, puis d'attendre sur le point de "je suis en cours de traitement" de l'événement. Si tout ce que vous faites des tests est que le processus est appelé alors vous pouvez définir un autre événement et laisser le fil de continuer. Si vous faites des tests de quelque chose de plus complexe, comme la file d'attente des poignées de multiples expédition ou de quelque chose, alors vous pourriez faire d'autres choses (comme la poste et attendre que les autres éléments de travail) avant de libérer le fil. Puisque vous pouvez vous attendre à un délai d'attente dans le test que vous pouvez vous assurer que (dire) que deux éléments de travail sont effectués en parallèle, etc, etc. La clé, c'est que vous faites les tests déterministes à l'aide d'événements que le filetage de blocs de code pour que le test permet de contrôler quand ils courent.
Je suis sûr que votre situation est plus complexe, mais ce est la méthode de base que j'utilise pour tester fileté code et il fonctionne assez bien. Vous pouvez prendre une quantité surprenante de contrôle multi-threaded code si vous vous moquez du droit de bits et de mettre la synchronisation des points.
Voici quelques infos sur ce genre de chose, bien qu'il parle d'une base de code C++: http://www.lenholgate.com/blog/2004/05/practical-testing.html
Mon conseil serait de ne pas s'appuyer sur des tests unitaires afin de détecter des problèmes de concurrence, et ce pour plusieurs raisons:
- Le manque de reproductibilité: les tests échouent qu'une seule fois dans un tout, et ne sera pas vraiment utile pour repérer les problèmes.
- Erratique à défaut de construire ennuyer tout le monde dans l'équipe - parce que le dernier commit sera toujours soupçonnée à tort d'être la cause de l'échec de construire.
- Les blocages lorsque rencontrés sont susceptibles de geler la construction jusqu'à ce que le délai d'exécution est rencontrés qui peut considérablement ralentir la construction.
- L'environnement est susceptible d'être un seul PROCESSEUR de l'environnement (pensez à construire en cours d'exécution sur une machine virtuelle) où des problèmes de concurrence, risque de ne jamais arriver - peu importe combien le temps de sommeil est réglé.
- Il va à l'encontre d'une certaine manière l'idée d'avoir des simples, isolés, les unités de la validation du code.
Si vous devez tester qu'un thread d'arrière-plan fait quelque chose, une technique simple que je trouve pratique est d'avoir une méthode WaitUntilTrue, qui ressemble à ceci:
bool WaitUntilTrue(Func<bool> func,
int timeoutInMillis,
int timeBetweenChecksMillis)
{
Stopwatch stopwatch = Stopwatch.StartNew();
while(stopwatch.ElapsedMilliseconds < timeoutInMillis)
{
if (func())
return true;
Thread.Sleep(timeBetweenChecksMillis);
}
return false;
}
Utilisé comme ceci:
volatile bool backgroundThreadHasFinished = false;
//run your multithreaded test and make sure the thread sets the above variable.
Assert.IsTrue(WaitUntilTrue(x => backgroundThreadHasFinished, 1000, 10));
De cette façon, vous n'avez pas besoin de mettre votre thread de test principal en veille pendant longtemps pour donner au thread d'arrière-plan le temps de terminer. Si l'arrière-plan ne se termine pas dans un délai raisonnable, le test échoue.
TypeMock (commercial) est un framework de test unitaire qui tente automatiquement de trouver les blocages dans les applications multithread et je pense que peut être configuré pour exécuter les threads prévisible, la commutation de contexte.
J'ai vu une démo cette semaine lors d'un salon-apparemment, il est en Alpha (appelé Racer)
http://www.typemock.com/Typemock_software_development_tools.html
Je suis tombé sur un produit de recherche, appelé Microsoft Chess . Il est spécialement conçu pour les tests non déterministes des applications multithread. L'inconvénient jusqu'à présent, c'est qu'il est intégré dans VS.