J'écris du code (récursif) qui navigue dans un graphe de dépendances recherche des cycles ou des contradictions dans les dépendances. Cependant, je ne suis pas sûr à l'unité approcher tester cela. Le problème est que l'une de nos principales préoccupations est que le code gère toutes les structures de graphiques intéressantes qui peuvent survenir et s'assure que tous les nœuds seront traités de manière appropriée.
Bien qu'une couverture de ligne ou de branche à 100% soit généralement suffisante pour être sûr que certains codes fonctionnent, il semble que même avec une couverture de chemin à 100%, vous ayez encore des doutes.
Alors, comment procéder pour sélectionner des structures de graphes pour les cas de test afin d'avoir l'assurance que leur code pourrait gérer toutes les permutations imaginables que vous trouverez dans les données du monde réel.
PS- Si c'est important, tous les bords de mon graphique sont étiquetés "doit avoir" xor "ne peut pas avoir" et il n'y a pas de cycles triviaux, et il n'y a qu'un seul bord entre deux nœuds.
PPS- L'énoncé de problème supplémentaire a été enregistré par l'auteur de la question dans un commentaire ci - dessous:
For all vertices N in forest F, for all vertices M, in F, such that if there are any walks between N and M they all must either use only edges labelled 'conflict' or 'requires'.
la source
Réponses:
Cela semble être un bon début. Je suppose que vous avez déjà essayé d'appliquer des techniques classiques comme analyse des valeurs limites ou partitionnement équivalence , car vous tests basés sur la couverture déjà parlé. Après vous avez investi beaucoup de temps dans la construction de cas bon test, vous arriverez à un point où vous, votre équipe et aussi testeurs (si vous avez les) à court d'idées. Et c'est l'endroit où vous devez quitter le chemin de l' unité test et commencer à tester avec des données beaucoup de monde réel que vous pouvez.
Peut-être que vous devez écrire des outils ou des programmes supplémentaires juste pour cette partie du processus. La partie difficile ici est probablement de vérifier l'exactitude de la sortie de vos programmes, lorsque vous mettez dix mille graphiques différents dans votre programme, comment saurez-vous si votre programme produit toujours la sortie correcte? De toute évidence, vous ne pouvez pas vérifier manuellement. Donc, si vous êtes chanceux, vous pourrez peut-être effectuer une deuxième implémentation très simple de votre contrôle de dépendance, qui pourrait ne pas répondre à vos attentes de performances, mais est plus facile à vérifier que votre algorithme d'origine. Vous devriez aussi essayer d'intégrer beaucoup de vérifications de vraisemblance directement dans votre programme (par exemple,
Enfin, apprenez à accepter que chaque test ne peut que prouver l'existence de bugs, mais pas l'absence de bugs.
la source
1. Génération de tests randomisés
Ecrire un algorithme qui génère des graphiques, l'ont générer quelques centaines (ou plus) des graphes aléatoires et jeter chacun à votre algorithme.
Conservez la graine aléatoire des graphiques qui provoquent des échecs intéressants et ajoutez-les en tant que tests unitaires.
2. Pièces difficiles à coder en dur
Quelques graphiques structures que vous savez sont difficiles, vous pouvez coder tout de suite, ou d'écrire un code qui combine eux et les pousse à votre algorithme.
3. Générer une liste exhaustive
Mais, si vous voulez être sûr « le code pourrait gérer toutes les permutations imaginables que vous trouverez dans les données du monde réel. », Vous devez générer ces données à partir de graines non au hasard, mais en marchant bien que toutes les permutations. (Cela se fait lors du test de systèmes de signalisation ferroviaire de métro, et vous donne d'énormes quantités de cas qui prend les âges à tester. Pour le métro de métro, le système est limité, donc il y a une limite supérieure au nombre de permutations. Je ne sais pas comment votre cas applique)
la source
For all vertices N in forest F, for all vertices M, in F, such that if there are any walks between N and M they all must either use only edges labelled 'conflict' or 'requires'.
Le domaine n'est pas la question.Aucun test ne pourra être suffisant dans ce cas, pas même des tonnes de données réelles ou de fuzzing. Une couverture de code à 100%, voire une couverture de chemin à 100% est insuffisante pour tester les fonctions récursives.
Soit la fonction récursive se lève une preuve formelle (ne devrait pas être si difficile dans ce cas), ou il ne fonctionne pas. Si le code est trop entrelacé avec du code spécifique à l'application pour exclure les effets secondaires, c'est par où commencer.
L'algorithme lui-même ressemble à un algorithme simple d'inondation, semblable à un simple large recherche d'abord, avec l'ajout d'une liste noire qui ne doit pas Intersection avec la liste des noeuds visités, exécutez de tous les nœuds.
Cet algorithme itératif satisfait à la condition qu'aucune dépendance peut être nécessaire et mis à l'index en même temps, pour les graphiques de la structure arbitraire, à partir de tout artefact arbitraire dans lequel l'artefact de départ est toujours nécessaire.
Bien qu'il peut ou ne peut pas être aussi rapide que votre propre implémentation, il peut être prouvé qu'elle se termine pour tous les cas (comme pour chaque itération de la boucle externe chaque élément ne peut être poussé une fois sur la
tovisit
file d' attente), il inonde l'ensemble accessible graphique (preuve inductive), et il détecte tous les cas où un artefact est appelé à être requis et une liste noire en même temps, à partir de chaque noeud.Si vous pouvez montrer que votre propre implémentation a les mêmes caractéristiques, vous pouvez prouver l'exactitude sans entraîner de tests unitaires. Seules les méthodes de base pour pousser et sauter des files d'attente, en comptant la longueur de la file d'attente, itérer sur les propriétés et ont tous besoin d'être testés et se sont révélés exempts d'effets secondaires.
EDIT: Ce que cet algorithme ne prouve pas, c'est que votre graphique est exempt de cycles. Des graphes acycliques sont un sujet bien documenté, donc trouver un algorithme tout fait pour prouver cette propriété devrait être facile aussi bien.
Comme vous pouvez le voir, il n'est pas du tout nécessaire de réinventer la roue.
la source
Vous utilisez des expressions comme «toutes les structures de graphiques intéressantes» et «gérées de manière appropriée». Sauf si vous avez les moyens de tester votre code contre toutes ces structures et déterminer si le code gère le graphique correctement, vous ne pouvez utiliser des outils tels que l'analyse de couverture de test.
Je vous suggère de commencer par la recherche et les essais avec un certain nombre de structures de graphiques intéressants et déterminer le traitement approprié serait et que le code fait ça. Ensuite, vous pouvez commencer ces graphiques en perturbant a) des graphiques brisés qui violent les règles ou b) des graphiques pas si intéressants qui ont des problèmes; voir si votre code ne parvient pas à les gérer correctement.
la source
Vous pouvez essayer de faire une sorte topologique et de voir si elle réussit. Si elle ne le fait pas, alors vous avez au moins un cycle.
la source
En ce qui concerne ce type d'algorithme difficile à tester, j'opterais pour le TDD, où vous construisez l'algorithme basé sur des tests,
TDD bref,
et répéter le cycle,
Dans cette situation particulière,
Un aspect important de cette méthode est que vous devez toujours ajouter un test pour l'étape possible (où vous divisez les scénarios possibles en tests simples), et lorsque vous couvrez tous les scénarios possibles, l'algorithme évolue généralement automatiquement.
Enfin, vous devez ajouter un ou plusieurs tests d'intégration compliqués pour voir s'il y a des problèmes imprévus (tels que des erreurs de débordement de pile / des erreurs de performances lorsque votre graphique est très volumineux et lorsque vous utilisez la récursivité)
la source
Ma compréhension du problème, tel qu'indiqué à l'origine puis mis à jour par les commentaires sous la réponse de Macke, comprend les éléments suivants: 1) les deux types de bord (dépendances et conflits) sont dirigés; 2) si deux nœuds sont connectés par un bord, ils ne doivent pas être connectés par un autre, même s'il est de l'autre type ou inversé; 3) si un chemin entre deux nœuds peut être construit en mélangeant des bords de différents types, alors c'est une erreur, plutôt qu'une circonstance qui est ignorée; 4) S'il y a un chemin entre deux nœuds utilisant des bords d'un type, alors il ne peut pas y avoir un autre chemin entre eux utilisant des bords d'un autre type; 5) cycles de soit un seul type de bord ou types bord mixtes ne sont pas autorisés (à partir d'une estimation à l'application, je ne suis pas sûr que les cycles de conflit ne sont une erreur, mais cette condition peut être enlevé, sinon.)
De plus, je suppose que la structure de données utilisée n'empêche pas que des violations de ces exigences soient exprimées (par exemple, un graphique violant la condition 2 ne peut pas être exprimé dans une carte de la paire de nœuds à (type, direction) si la paire de nœuds est toujours a le noeud moins numéroté en premier.) Si certaines erreurs ne peuvent être exprimées, il réduit à considérer le nombre de cas.
Il y a en fait trois graphiques qui peuvent être considérés ici: les deux exclusivement un type de bord, et le graphique mixte formé par l'union d'un de chacun des deux types. Vous pouvez l'utiliser pour générer systématiquement tous les graphiques jusqu'à un certain nombre de nœuds. Générez d'abord tous les graphiques possibles de N nœuds n'ayant pas plus d'un bord entre deux paires ordonnées de nœuds (paires ordonnées car ce sont des graphiques dirigés.) Prenez maintenant toutes les paires possibles de ces graphiques, l'une représentant les dépendances et l'autre représentant les conflits, et former l'union de chaque paire.
Si votre structure de données ne peut pas exprimer les violations de la condition 2, vous pouvez réduire considérablement les cas à considérer en construisant uniquement tous les graphiques de conflit possibles qui tiennent dans les espaces des graphiques de dépendance, ou vice-versa. Dans le cas contraire, vous pouvez détecter les violations de l'état 2 tout en formant l'union.
Sur une traversée en largeur du graphique combiné à partir du premier nœud, vous pouvez créer l'ensemble de tous les chemins vers chaque nœud accessible et, ce faisant, vous pouvez vérifier les violations de toutes les conditions (pour la détection de cycle, vous pouvez utiliser l'algorithme de Tarjan .)
Il vous suffit de considérer les chemins du premier noeud, même si le graphique est déconnecté, car les chemins de tout autre noeud apparaissent comme les chemins du premier noeud dans un autre cas.
Si les chemins à bords mixtes peuvent simplement être ignorés, plutôt que d'être des erreurs (condition 3), il suffit de considérer les graphiques de dépendance et de conflit indépendamment et de vérifier que si un nœud est accessible dans l'un, il ne l'est pas dans l'autre.
Si vous vous rappelez les chemins trouvés dans l'examen des graphiques des nœuds N-1, vous pouvez les utiliser comme point de départ pour générer et graphiques évaluation des noeuds N.
Cela ne génère pas de multiples arêtes du même type entre les nœuds, mais il pourrait être étendu à le faire. Cela augmenter considérablement le nombre de cas cependant, il serait préférable que l'être de code testé a fait cette impossible de représenter ou, à défaut, filtré tous les cas au préalable.
La clé pour écrire un oracle comme celui-ci est de le garder aussi simple que possible, même si cela signifie être inefficace, afin que vous puissiez lui faire confiance (idéalement par le biais d'arguments pour son exactitude, étayés par des tests.)
Une fois que vous avez les moyens de générer des cas de test et que vous faites confiance à l'oracle que vous avez créé pour séparer avec précision le bon du mauvais, vous pouvez l'utiliser pour piloter le test automatisé du code cible. Si cela n'est pas possible, votre prochaine meilleure option consiste à passer en revue les résultats pour les cas distinctifs. L'oracle peut classer les erreurs qu'il trouve et vous donner des informations sur les cas acceptés, tels que le nombre et la longueur des chemins de chaque type, et s'il y a des nœuds au début des deux types de chemin, et cela pourrait vous aider à rechercher des cas que vous n'avez jamais vus auparavant.
la source