Cet article a fait l'objet d'une traduction automatique.
Série de tests
Test par paire avec QICT
James McCaffrey
Une connaissance solide de test pairwise principes est essentielle pour tous les testeurs de logiciels, développeurs et responsables. Dans mois-ci ce, j'expliquer quels pairwise test est exactement et vous fournir un code de source complète c# pour une qualité production pairwise outil nommé QICT de test. En bref, pairwise test est une technique qui vous permet de réduire un jeu volumineux, ingérable d'entrées de scénario de test à un ensemble beaucoup plus petite est susceptible de révéler des problèmes dans le système testé. Il est le meilleur moyen pour expliquer pairwise test et pour vous montrer où je suis à têtes dans cet article, par le biais de deux captures d'écran. Envisagez de l'application basée sur un Windows Form factice illustrée à la de la figure 1. L'application comporte quatre paramètres d'entrée. Le premier paramètre est un contrôle TextBox qui peut accepter “ b respectez ou “ respectez. Le deuxième paramètre est un groupe de contrôles RadioButton qui peut prendre la valeur de “ c respectez, “ d respectez, “ e respectez ou “ f respectez. Le troisième paramètre est un contrôle ComboBox qui peut prendre la valeur de “ g, respectez “ h respectez ou “ i respectez. Le quatrième paramètre est un contrôle CheckBox qui prend une valeur de “ j respectez ou “ k respectez. Pour un jeu d'entrée de scénario de test serait {“ un respectez, “ c respectez, “ g respectez, “ j respectez}. L'application factice possède un total de 2 * 4 * 3 * 2 = 48 possible d'entrée de jeux, qui est certainement gérable. Mais, imaginez une application de lecture de carte quelconque avec cinq paramètres, où chaque paramètre peut prendre une des 52 valeurs (pour représenter une carte à partir d'un normal jeu de cartes, avec remplacement). Dans ce cas il serait possible 52 * 52 * 52 * 52 * 52 = 380,204,032 entrée jeux, qui est susceptible d'être ingérable, à moins que vous pouvez générer par programme des valeurs attendues pour chaque test de jeu d'entrée.
Figure 1 de la A factice application avec quatre paramètres d'entrée
L'idée de pairwise test consiste à générer une liste des jeux de test qui capture toutes les paires de valeurs de paramètre à partir de chaque paramètre possibles. Pour l'exemple illustré dans de la figure 1, il existe un total de 44 de telles paires d'entrée :
(a,c), (a,d), (a,e), (a,f), (a,g), (a,h), (a,i), (a,j), (a,k), (b,c), (b,d), (b,e), (b,f), (b,g), (b,h), (b,i), (b,j), (b,k), (c,g), (c,h), (c,i), (c,j), (c,k), (d,g), (d,h), (d,i), (d,j), (d,k), (e,g), (e,h), (e,i), (e,j), (e,k), (f,g), (f,h), (f,i), (f,j), (f,k), (g,j), (g,k), (h,j), (h,k), (i,j), (i,k)
Maintenant le test {“ un respectez, “ c respectez, “ g respectez, “ j respectez} capture six des 44 paires : (a, c) (a, g), (a, j), (c, g), (c, j) et (g, j). L'objectif de génération de test pairwise définie est donc pour produire une collection de jeux de test qui capture toutes les 44 paires. Observez la capture d'écran dans de la figure 2.
La figure 2 Test Pairwise Set génération avec l'outil QICT
La capture d'écran illustre un outil nommé qict.exe génération d'une collection de 12 jeux de test qui capture toutes les paires d'entrée 44 pour le scénario illustré à la de la figure 1. Si vous suivi par le biais de chaque paire de valeurs dans les 12 jeux de test générés dans de la figure 2, vous verrez qu'ils en fait capturer toutes les 44 paires répertoriés ci-dessus. Par conséquent, dans ce cas, nous ont réduit notre entrées de scénario de test possibles de 48 scénarios de test à 12 scénarios de test. Les économies ne sont pas très significatifs pour ce petit exemple mais comme je le montrerai dans un moment, à l'aide de test pairwise peut réduire considérablement le nombre d'entrées de scénario de test dans de nombreuses situations. L'hypothèse de test pairwise sous-jacent est que les bogues logiciels sont trouvent plus fréquemment dans le code qui implique l'interaction des valeurs à partir des paramètres différents que dans le code qui implique des valeurs à partir d'un paramètre particulier. En d'autres termes, pour l'application factice dans de la figure 1, code d'application qui traite des entrées “ g respectez et “ respectez est plus susceptible d'introduire une erreur logique que le code qui traite des entrées “ b respectez et “ respectez. Il s'agit d'une notion qui est, en fait, prise en charge par des recherches.
À l'aide de l'outil de PICT
Il existe plusieurs outils de génération de jeu pairwise test pour vous. Mon outil préféré dans la plupart des cas est l'outil PICT (Pairwise Independent Combinatorial test). PICT a été écrit par mon collègue Jacek Czerwonka, qui a adapté le code à partir d'un outil de paire interne Microsoft existant. PICT est disponible en téléchargement gratuit à partir de plusieurs emplacements, y compris la page Centre de tests de Microsoft à msdn.microsoft.com/testing/bb980925.aspx de . Si vous effectuez une recherche Internet, vous trouverez également que plusieurs autre test pairwise définie des outils de génération. Toutefois, PICT est un exécutable unique qui s'exécute à partir d'une ligne de commande shell. PICT est très rapide, très puissant et doit répondre à vos pairwise tests nécessaires dans la plupart des situations. J'ai nommé l'outil présenté dans cet article QICT (qui ne représentent rien en particulier) pour accuser réception de l'importance de l'outil PICT.
Pourquoi c'est le cas, mais un autre test pairwise définie Générateur ? Il existe plusieurs raisons. Tout d'abord, bien que PICT est un outil formidable, elle est écrite en code C++ natif et le code source n'est pas disponible. L'outil QICT présentée ici est, pour autant que je peut dire, le premier outil de paire de qualité production écrit avec du code managé c#. La disponibilité du code permet de modifier librement QICT pour répondre à vos propres besoins. Par exemple, vous pouvez modifier QICT pour lire directement son entrée à partir d'un fichier XML ou d'une base de données SQL, ou vous pouvez modifier QICT pour émettre directement les résultats dans un format de sortie personnalisée. Et vous souhaitez expérimenter logique de l'outil, disons, par exemple, en introduisant des contraintes (test de jeux d'entrée qui n'est pas autorisés), en introduisant des jeux de test requis, ou modifier la façon dont l'outil génère son test collection de jeu. En outre, la disponibilité du code source QICT permet la que copie et de paire place tester le code de génération définie directement dans un outil de test ou de l'application .NET. Enfin, bien que le code source pour quelques outils de génération de jeu de test par paire est disponible sur Internet, certains de ces outils sont très inefficaces. Par exemple, examinons une situation avec 20 paramètres, chacun possédant 10 valeurs. Pour ce scénario, il y a 10 * 10 * 10 *. . . * 10 (20 fois) = entrées de scénario de test possibles 1020 = 100,000,000,000,000,000,000. Il s'agit d'un grand nombre de scénarios de test. L'outil PICT cela réduit aux jeux de test par paire uniquement 217 et l'outil QICT produit 219 ou 216 jeux de test (selon la valeur de départ d'un générateur de nombres aléatoires, comme je l'expliquerai bientôt). Toutefois, une référencées largement génération des tests pairwise définie outil écrit en Perl génère 664 jeux. Enfin, avec le code source QICT disponible et explication sur de cet article les algorithmes utilisés, vous pouvez refondu QICT à d'autres langages, tels que Perl, Python, Java ou JavaScript si vous le souhaitez.
L'outil de QICT
Le code de l'outil QICT est légèrement trop long pour présenter dans son intégralité dans cette colonne, mais le code source complet est disponible à partir de MSDN Code Gallery à code.msdn.microsoft.com. Je vais décrire les algorithmes et structures de données que j'utilise, ainsi que des extraits de code de touche, afin que vous avez suffisamment d'informations à utiliser et de modifier QICT selon vos besoins. L'essence même du fonctionnement de QICT consiste à générer un ensemble de test à la fois, à l'aide d'algorithmes gourmands pour placer chaque valeur de paramètre, jusqu'à ce que toutes les paires possibles ont été capturées. L'algorithme de haut niveau pour QICT est présenté dans de la figure 3.
De la figure 3 algorithme QICT
read input file
create internal data structures
create an empty testset collection
while (number of unused pairs > 0)
for i := 1 to candidate poolSize
create an empty candidate testset
pick the "best" unused pair
place best pair values into testset
foreach remaining parameter position
pick a "best" parameter value
place the best value into testset
end foreach
end for
determine "best" candidate testset
add best testset to testset collection
update unused pairs list
end while
display testset collection
La clé de l'implémentation de cet algorithme de haut niveau consiste à déterminer quel type de structures de données à utiliser et quels sont les différentes options “ meilleures respectez. Le code source QICT commence comme suit :
static void Main(string[] args)
{
string file = args[0];
Random r = new Random(2);
int numberParameters = 0;
int numberParameterValues = 0;
int numberPairs = 0;
int poolSize = 20;
J'ai codé QICT à l'aide d'un style traditionnel des procédures plutôt que de prendre une approche orientée objet afin que vous pouvez plus facilement refactoriser QICT aux langages avec un support limité de programmation orientée objet, tels que Perl et JavaScript. J'ai tout d'abord lire un fichier d'entrée à partir de la ligne de commande. Comme vous pouvez le voir, pour que mon code reste en mode minimal et simple, vous avez omis normale-vérification des erreurs que vous seriez amené à inclure. Le fichier d'entrée pour QICT est identique à celui utilisé par PICT, un simple fichier texte qui ressemble à :
Param0 : a, b
Param1 : c, d, e, f
etc..
Noms de paramètres sont suivies par un signe deux-points et d'une liste délimitée par des virgules de valeurs autorisées pour ce paramètre. Les valeurs de paramètre doivent être distincts. Ensuite, j'instancie un objet Random. Le choix d'une valeur initiale de 2 est arbitraire, mais n'importe quelle valeur rendra QICT produisent le même résultat pour un jeu d'entrée chaque fois qu'il est exécuté. Je Vais vous expliquer l'objectif de l'objet de nombre pseudo-aléatoires peu de temps. Je déclare trois variables int qui seront affectés valeurs lorsque le fichier d'entrée est en lecture. Pour l'exemple illustré dans de la figure 2, numberParameters est de 4, numberParameterValues est 11 et numberPairs 44. La variable poolSize stocke le nombre de jeux de test de candidat pour générer pour chaque ensemble de test. Si vous le testez QICT un peu, vous verrez que l'outil est affectée de façon plutôt étonnamment secondaire en ajustant la valeur pour poolSize. Le cœur de QICT est la déclaration des structures de données principale. Les quatre premiers objets sont les suivants :
int[][] legalValues = null;
string[] parameterValues = null;
int[,] allPairsDisplay = null;
List<int[]> unusedPairs = null;
L'objet legalValues est un tableau en escalier où chaque cellule conserve à son tour un tableau de valeurs int. Le tableau legalValues contient une représentation en mémoire du fichier d'entrée, afin de la cellule 0 des valeurs admises conserve un tableau qui contient à son tour les valeurs 0 (pour représenter la valeur du paramètre “ un respectez) et 1 (pour représenter “ b respectez). Il s'avère que travailler directement avec les valeurs de chaîne est plutôt inefficace et que représentant les valeurs de paramètre en tant qu'entiers procure des performances beaucoup plus rapide. Le tableau de chaînes parameterValues conserve les valeurs de paramètre réel et est utilisé à la fin de QICT pour afficher les résultats sous forme de chaînes plutôt que des entiers (ints). Par conséquent, pour l'exemple précédent, cellule 0 conserve “ k respectez, cellule 1 conserve “ b respectez et ainsi de suite à la cellule 10, qui contient “ respectez. L'objet allPairsDisplay est un tableau d'entiers à deux dimensions. Il est rempli par toutes les paires possibles. Pour notre exemple, cellule [0,0] conserve 0 (pour “ un respectez) et la cellule contient 2 [0,1] (pour “ c respectez) — la première paire possible. Cellule [1,0] conserve 0 et la cellule contient [1,1] 3 pour représenter la deuxième paire (a, d). L'objet unusedPairs est une liste générique de tableaux de type int. Le premier élément dans unusedPairs est initialement {0,2}. J'utilise une collection List pour unusedPairs plutôt qu'un tableau parce que chaque fois qu'un nouvel ensemble de test est ajouté au test définit la collection, je supprime les paires générés par le nouveau test définir à partir d'unusedPairs. En outre, cela signifie que j'ai une condition d'arrêt pratique qui se produira lorsque unusedPairs.Count atteint 0.
Les quatre structures de données de programme principal sont :
int[,] unusedPairsSearch = null;
int[] parameterPositions = null;
int[] unusedCounts = null;
List<int[]> testSets = null;
Plus pairwise tester les outils de génération de jeu, y compris QICT, effectuer un très grand nombre de recherches. Une approche de recherche efficace est indispensable pour des performances raisonnables. Ici, je déclare un tableau à deux dimensions nommé unusedPairsSearch. Il est une matrice carrée avec taille numberParameterValues par numberParameterValues, où chaque cellule conserve un 1 si la paire correspondante n'a pas été utilisée et un 0 si la paire correspondante a été utilisée ou n'est pas une paire valide. Initialement, les trois premières lignes d'unusedPairsSearch pour l'exemple dans la figure 2 sont les suivantes :
0 0 1 1 1 1 1 1 1 1 1
0 0 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 1 1 1 1 1
Et ainsi de suite.
Ligne 1 signifie paires (0,0) et (0,1) — c'est-à-dire (un, un) et (a, b) — ne sont pas valides, tout en paires (0,2), (0,3). . . (0,10) — c'est-à-dire (a, c) (a, d) à (a, k) — n'ont pas encore été capturées par un ensemble de test. Le tableau parameterPositions contient l'emplacement au sein d'un jeu de test d'une valeur de paramètre spécifié. Après l'initialisation de ce tableau conserve des valeurs :
0 0 1 1 1 1 2 2 2 3 3
L'index de parameterPositions représente une valeur de paramètre et la valeur correspondante de la cellule représente sa position dans un ensemble de test. Afin que la quatrième cellule de gauche à droite ait index = 3 et valeur = 1, ce qui signifie que la valeur du paramètre 3 (“ d respectez) appartient à la position 1 (le deuxième emplacement) dans un ensemble de test. L'objet unusedCounts est un tableau unidimensionnel qui conserve le nombre de fois qu'une valeur de paramètre particulier apparaît dans le tableau unusedPairs. Initialement unusedCounts contient :
9 9 7 7 7 7 8 8 8 9 9
L'index représente une valeur de paramètre et la valeur correspondante de la cellule est le nombre inutilisé. Par conséquent, la quatrième cellule de gauche à droite a index = 3 et valeur = 7, initialement ce qui veut dire que la valeur du paramètre 3 (“ d respectez) apparaît dans 7 inutilisés paires — (a, d), (b, d), (d, g), (d, h), (d, I), (d, j) et (d, k). L'objet testSets contient les résultats du jeu de test par paire. Il est initialement vide mais augmente chaque fois qu'un nouvel ensemble de test est généré. Chaque jeu de test est représenté par un tableau int. Par conséquent, dans la figure 2, le premier test défini dans le résultat est {“ un respectez, “ c respectez, “ g respectez, “ j respectez}, qui est stockée dans la liste testSets sous la forme d'un tableau avec des valeurs {0,2,6,9}.
Avec les structures de données de clé en place, QICT lit le fichier d'entrée pour déterminer les valeurs pour numberParameters et numberParameterValues et pour remplir les tableaux legalValues et parameterValues. J'utilise l'approche relativement grossier d'effectuer une lecture initiale du fichier, puis réinitialiser le pointeur de fichier et effectuer un deuxième passage par le biais du fichier. Une fois legalValues est remplie, vous pouvez numériser traversé pour déterminer le nombre de paires pour l'entrée :
for (int i = 0; i <= legalValues.Length - 2; ++i) {
for (int j = i + 1; j <= legalValues.Length - 1; ++j) {
numberPairs += (legalValues[i].Length * legalValues[j].Length);
}
}
Console.WriteLine("\nThere are " + numberPairs + " pairs ");
Après l'initialisation, la première ligne de legalValues conserve {0,1} et la deuxième ligne conserve {2,3,4,5}. Notez que les paires déterminé par ces deux lignes sont (0,2), (0,3), (0,4), (0,5), (1,2), (1,3), (1,4) et (1,5), et qu'en général le nombre de paires déterminé par les deux lignes dans legalValues est le produit du nombre de valeurs dans les deux lignes, ce qui équivaut à la propriété de longueur des lignes de la ligne. La partie suivante de code QICT remplit la liste unusedPairs :
unusedPairs = new List<int[]>();
for (int i = 0; i <= legalValues.Length - 2; ++i) {
for (int j = i + 1; j <= legalValues.Length - 1; ++j) {
int[] firstRow = legalValues[i];
int[] secondRow = legalValues[j];
for (int x = 0; x < firstRow.Length; ++x) {
for (int y = 0; y < secondRow.Length; ++y) {
int[] aPair = new int[2];
aPair[0] = firstRow[x];
aPair[1] = secondRow[y];
unusedPairs.Add(aPair);
}
}
}
}
Ici j'emparer chaque paire de lignes à partir de legalValues utilisant indexe i et j. Ensuite, j'en revue les valeurs dans chaque paire de ligne à l'aide d'index x et y. Utilisation intensive de plusieurs imbriqué des boucles for comme ceci est une référence dans du code combinatorial. Lorsque vous écrivez un tel code, je dessine toujours à la main sur une feuille de papier les tableaux complexe, car il est très facile de commettre sans un diagramme. Après le remplissage de la liste unusedPairs, j'utilise la même structure de boucle imbriquée pour remplir les tableaux allPairsDisplay et unusedPairsSearch. Le code d'initialisation remplit ensuite le tableau parameterPositions en effectuant une itération à legalValues :
parameterPositions = new int[numberParameterValues];
int k = 0;
for (int i = 0; i < legalValues.Length; ++i) {
int[] curr = legalValues[i];
for (int j = 0; j < curr.Length; ++j) {
parameterPositions[k++] = i;
}
}
Le code d'initialisation conclut en remplissant le tableau unusedCounts :
unusedCounts = new int[numberParameterValues];
for (int i = 0; i < allPairsDisplay.GetLength(0); ++i) {
++unusedCounts[allPairsDisplay[i, 0]];
++unusedCounts[allPairsDisplay[i, 1]];
}
Ici, comme dans la plupart des routines QICT, je tirer parti du fait que c# automatiquement initialise toutes les cellules de tableaux d'int à 0. Si vous souhaitez refondu QICT à un style orienté objet, toutes ces routines d'initialisation aurait probablement mieux être placés dans un constructeur d'objet ou peut-être dans une méthode Initialize() explicite. Le traitement principal début de la boucle :
testSets = new List<int[]>();
while (unusedPairs.Count > 0) {
int[][] candidateSets = new int[poolSize][];
for (int candidate = 0; candidate < poolSize; ++candidate) {
int[] testSet = new int[numberParameters];
// fill candidate testSets
}
// copy best testSet into testSets collection; upate data structues
}
Puisque le nombre de jeux de test de candidat est connu pour être poolSize, I pouvez instancier un tableau plutôt que d'utiliser un objet List de taille dynamique. Notez que la taille de la collection unusedPairs contrôle la sortie de boucle de traitement principal. Maintenant il est temps pour choisir la paire inutilisée “ mieux respectez — et maintenant les choses commencent à devenir vraiment intéressant :
int bestWeight = 0;
int indexOfBestPair = 0;
for (int i = 0; i < unusedPairs.Count; ++i) {
int[] curr = unusedPairs[i];
int weight = unusedCounts[curr[0]] + unusedCounts[curr[1]];
if (weight > bestWeight) {
bestWeight = weight;
indexOfBestPair = i;
}
}
Ici je définis mieux pour signifier la paire inutilisée de la somme des valeurs de paramètre individuel inutilisé le plus élevée. Par exemple, si “ a respectez apparaît une seule fois dans la liste actuelle des paires inutilisés, “ b respectez apparaît deux fois, “ c respectez trois fois et “ d respectez quatre fois, puis paire (a, c) a poids 1 + 3 = 4, et paire (b, d) a poids (b, d) 2 + 4 = 6, afin de la paire (b, d) sera exécuté plutôt (a, c).
Il existe de nombreuses autres schémas de pondération que vous souhaiterez peut-être Explorer. Par exemple, à l'aide de multiplication quelconque donnerait poids supérieures aux paires avec les valeurs extrêmes de décomptes inutilisés par rapport aux paires ayant inutilisé compte ensemble plus proche. Une autre possibilité consiste à effectuer le suivi des décomptes utilisés, le nombre de fois où les valeurs de paramètre apparaissent dans les jeux de tests déjà ajoutés à la collection de testSets résultat — et choisir comme le meilleur paire celle qui a les nombres de moins utilisées. Une fois que la paire inutilisée mieux a été déterminée, je crée un tableau de deux cellules pour contenir les valeurs de paire et définir la position au sein d'un jeu de test auquel appartient chaque valeur :
int[] best = new int[2];
unusedPairs[indexOfBestPair].CopyTo(best, 0);
int firstPos = parameterPositions[best[0]];
int secondPos = parameterPositions[best[1]];
À ce stade je possède un ensemble de tests vide et une paire de valeurs à placer dans le jeu de test, et que je connais l'emplacement dans le jeu de test auquel appartiennent les valeurs. L'étape suivante consiste à générer des valeurs de paramètre pour les positions restantes dans le jeu de test. Maintenant, au lieu de remplir les positions de jeu de test dans un ordre fixe (à partir d'index de faible à élevé), il se trouve qu'il est préférable de remplir le test défini dans un ordre aléatoire. Tout d'abord, je génère un tableau qui contient les positions de paramètre dans un ordre séquentiel :
int[] ordering = new int[numberParameters];
for (int i = 0; i < numberParameters; ++i)
ordering[i] = i;
Ensuite, je réorganiser l'ordre en plaçant les emplacements connus des deux premières valeurs à partir de la paire de méthodes dans les deux premières cellules du tableau de commande :
ordering[0] = firstPos;
ordering[firstPos] = 0;
int t = ordering[1];
ordering[1] = secondPos;
ordering[secondPos] = t;
Et maintenant j'aléatoire les positions restantes (à partir de la cellule 2 et haut) à l'aide de l'algorithme de lecture aléatoire de Knuth. C'est pourquoi j'ai créé un objet Random au début du code QICT. Le nombre de jeux de test produit par QICT est étonnamment sensible à la valeur de la valeur initiale Générateur de nombre pseudo-aléatoires, afin que vous souhaitez expérimenter plusieurs valeurs de départ. Pour la situation avec des paramètres de valeur de 10 20, que j'ai décrit précédemment, à l'aide d'une valeur initiale la valeur 2 génère 219 jeux de test et une valeur de départ de 6 génère 216 ensembles de test, mais une valeur de départ de 0 génère 221 ensembles de test.
for (int i = 2; i < ordering.Length; i++) {
int j = r.Next(i, ordering.Length);
int temp = ordering[j];
ordering[j] = ordering[i];
ordering[i] = temp;
}
Après la permutation, je place les deux valeurs à partir de la paire de méthodes dans le jeu de tests du candidat :
testSet[firstPos] = best[0];
testSet[secondPos] = best[1];
Passons la partie la plus importante de l'algorithme QICT. Je doit déterminer les valeurs de paramètre meilleurs à placer dans chacun des positions de jeu de tests vide. La technique que j'utilise est une autre approche exigeant en ressources. Pour chaque position de paramètre, je teste chaque valeur possible conforme à cette position, en comptant le nombre de paires inutilisés dans la valeur de test, lorsqu'elle est combinée avec les autres valeurs déjà présentes dans le test définir capture. Puis je sélectionne la valeur du paramètre qui capture les paires plus inutilisés. Le code pour ce faire est la partie trickiest de QICT et répertorié dans de la figure 4.
La figure 4 remplissage Test Set avec les valeurs de paramètre
for (int i = 2; i < numberParameters; ++i) {
int currPos = ordering[i];
int[] possibleValues = legalValues[currPos];
int currentCount = 0;
int highestCount = 0;
int bestJ = 0;
for (int j = 0; j < possibleValues.Length; ++j) {
currentCount = 0;
for (int p = 0; p < i; ++p) {
int[] candidatePair = new int[] { possibleValues[j],
testSet[ordering[p]] };
if (unusedPairsSearch[candidatePair[0], candidatePair[1]] == 1 ||
unusedPairsSearch[candidatePair[1], candidatePair[0]] == 1)
++currentCount;
}
if (currentCount > highestCount) {
highestCount = currentCount;
bestJ = j;
}
}
testSet[currPos] = possibleValues[bestJ];
}
La boucle extérieure dans de la figure 4 est le nombre total de test ensemble positions (accordé par numberParameters), moins de deux (parce que deux points sont utilisés par la paire de méthodes). À l'intérieur de cette boucle, j'extrais la position de la zone en cours pour remplir en recherchant dans le tableau de commande créé précédemment. La variable currentCount contient le nombre de paires inutilisés capturées par la valeur de paramètre de test. Notez que parce que je suis rempli test ensemble positions dans un ordre aléatoire, la paire de candidat de valeurs peut être désordonnée, donc j'ai besoin de vérifier les deux possibilités lorsque j'effectue une recherche dans le tableau unusedPairsSearch. À la fin du code dans de la figure 4, j'aura un candidat ayant des valeurs dans chaque position qui ont été sélectionnées à l'aide d'algorithmes gourmands de test. Maintenant j'ajoute simplement ce test de candidat défini dans la collection des candidats :
candidateSets[candidate] = testSet;
À ce stade, j'ai n = test de candidat poolSize jeux et j'ai besoin sélectionner le meilleur de ces options pour ajouter dans la collection de résultats principal testSet. Vous pourriez supposer que le premier jeu de test de candidat capture les paires plus inutilisés et simplement effectuer une itération dans chaque candidat commençant à la position 0, mais là encore, introduire certains caractère aléatoire produit les meilleurs résultats. Choisissez un endroit aléatoire dans les candidats et de le supposer qu'il s'agit du meilleur candidat :
int indexOfBestCandidate = r.Next(candidateSets.Length);
int mostPairsCaptured =
NumberPairsCaptured(candidateSets[indexOfBestCandidate],
unusedPairsSearch);
Ici, j'utilise une petite fonction d'assistance nommée NumberPairsCaptured() pour déterminer combien de paires inutilisées sont capturés par un ensemble de test donné. La fonction d'assistance est :
static int NumberPairsCaptured(int[] ts, int[,] unusedPairsSearch)
{
int ans = 0;
for (int i = 0; i <= ts.Length - 2; ++i) {
for (int j = i + 1; j <= ts.Length - 1; ++j) {
if (unusedPairsSearch[ts[i], ts[j]] == 1)
++ans;
}
}
return ans;
}
Maintenant je parcourez chaque jeu de test de candidat, suivi de l'emplacement de celle qui capture les paires plus inutilisés :
for (int i = 0; i < candidateSets.Length; ++i) {
int pairsCaptured = NumberPairsCaptured(candidateSets[i],
unusedPairsSearch);
if (pairsCaptured > mostPairsCaptured) {
mostPairsCaptured = pairsCaptured;
indexOfBestCandidate = i;
}
}
Et maintenant je copie le meilleur test de candidat défini dans l'objet de liste de résultats principal testSets :
int[] bestTestSet = new int[numberParameters];
candidateSets[indexOfBestCandidate].CopyTo(bestTestSet, 0);
testSets.Add(bestTestSet);
À ce stade, j'ai ont générés et j'ai ajouté un nouvel ensemble de test, afin que vous devez mettre à jour toutes les structures de données qui sont affectés, à savoir l'unusedPairs liste (en supprimant toutes les paires sont générés par le nouveau jeu de test), le tableau unusedCounts (en décrémentant le nombre pour chaque valeur de paramètre dans le nouveau jeu de test) et la matrice d'unusedPairsSearch (en retournant les valeurs associées à chaque paire généré par le nouveau test à partir de 1 la valeur 0).
Je suis maintenant à la fin de ma boucle de traitement principal. Je continue la génération des candidats, en sélectionnant le meilleur candidat, ajout de la meilleure version à testSets et opérations de structures de données de mise à jour. Le traitement se terminera lorsque le nombre de paires inutilisés atteint 0.
Puis j'afficher le résultat final :
Console.WriteLine("\nResult testsets: \n");
for (int i = 0; i < testSets.Count; ++i) {
Console.Write(i.ToString().PadLeft(3) + ": ");
int[] curr = testSets[i];
for (int j = 0; j < numberParameters; ++j) {
Console.Write(parameterValues[curr[j]] + " ");
}
Console.WriteLine("");
}
Console.WriteLine("");
}
Comme je l'ai mentionné précédemment, si vous modifiez QICT pour l'adapter à votre propre scénario de test particulier, vous souhaiterez peut-être émettre les résultats directement à un fichier XML, une base de données SQL ou une autre forme de stockage.
Produire une meilleure Systems
Pairwise test est une technique combinatorial avec facteurs probabiliste. Génération de test Pairwise définie est une importante technique, mais il n'est pas magique. N'oubliez pas que pairwise techniques réduisent simplement le nombre d'entrées de scénario de test dans le cas où vous disposez simplement trop de scénarios de test à traiter. Génération de test Pairwise définie ne crée pas de scénario de test des résultats attendus. Vous devez toujours commencer par utiliser les principes de tests normales, telles que la recherche à des conditions de limite, utiliser une entrée aléatoire pure et ainsi de suite, puis utilisez pairwise test pour compléter votre scénario de test génération. En outre, en règle générale, test plus est préférable, donc il n'y a aucune raison pourquoi vous ne pouvez pas ajouter des entrées de scénario de test supplémentaires à ceux générés par les outils de génération par paire. Bien que pairwise test est utile dans de nombreux cas, veillez à utiliser uniquement lorsque cela est nécessaire.
Vous avez trouvé pairwise test génération défini pour être très utile pour les tests de configuration pour le module de test des méthodes qui acceptent des valeurs énumérées et pour les bases de données SQL tests où chaque colonne dans une table a un nombre relativement restreint de valeurs différentes. Pairwise test n'est pas nécessairement une bonne approche pour les scénarios où vous avez un petit nombre d'entrées de scénario de test, ou lorsque vous pouvez produire par programme des résultats de test cas attendu (et par conséquent traiter avec un vaste ensemble d'entrée de scénario de test). Et pairwise test n'est pas utilisable normalement lorsque les valeurs d'entrée pour le système testé ne sont pas discrets. Toutefois, même dans les situations où le nombre de valeurs de paramètres possibles est très volumineux, vous pouvez peut-être utiliser efficacement le scénario de test par paire d'entrée de génération en séparant les valeurs de paramètre dans les classes d'équivalence. Lorsqu'elle est utilisée correctement, pairwise défini la génération des tests est une importante technique qui peut aider à vous produire une meilleure systèmes logiciels.
Dr. James McCaffrey fonctionne pour Volt Information Sciences Inc., où il gère la formation technique d'ingénieurs logiciels basé à Redmond, Washington, campus. Microsoft de Il a travaillé sur plusieurs produits Microsoft, y compris Internet Explorer et MSN Search et est l'auteur de .NET test Automation Recipes : Une approche du problème-solution(Apress, 2006). Vous pouvez contacter James à l'adresse jmccaffrey@volt.com ou v-jammc@microsoft.com.
Grâce à l'expert technique suivante pour la révision de cet article : Jacek Czerwonka
Envoyez vos questions et commentaires à l'attention de James à testrun@microsoft.com de.