Cet article a fait l'objet d'une traduction automatique.
Tests
Module de .NET test avec IronPython
James McCaffrey
Téléchargement de code disponible de la bibliothèque de code MSDN
Parcourir le code en ligne
Contenu
Le module en test
Ad hoc interactive module test
Léger module test Automation
Conclusion
Le langage de script Python propose de fonctionnalités qui rendent un choix excellent pour effectuer plusieurs types de test logiciel. Il plusieurs implémentations de Python disponibles notamment CPython, l'implémentation courante sur ordinateurs exécutant des systèmes d'exploitation semblable à UNIX et IronPython, une implémentation publiée en 2006 au plus tard que cibles Microsoft .NET Framework. Dans mois-ci ce, je vous montrent comment utiliser IronPython pour tester les modules basées sur .NET à partir de deux la ligne de commande Python et dans les scripts Python légers. Je suppose que vous avez une expérience avec un langage de script comme JavaScript, Windows PowerShell, VBScript, Perl, PHP ou Supposons Ruby, mais je ne pas que vous ayez aucune expérience avec Python. Observez figure 1 pour faire une idée de je suis têtes. J'utilise IronPython pour effectuer un test rapide et ad hoc d'une méthode qui compare deux mains carte dans un module de .NET nommé TwoCardPokerLib. J'AI décrit les commandes affichées dans la figure 1 en détail plus loin dans cette colonne, mais pour l'instant observer que J'AI interactive ajouter une référence à une DLL qui héberge mon module .NET, instancier des deux objets disponibles dans ce module et appeler une méthode comparaison pour déterminer si une main bat autre la main. Maintenant examinons rapidement figure 2 . Cette capture d'écran affiche l'exécution d'un script IronPython léger qui a pris du minutes uniquement à créer. Là encore, je vais sur tous les détails ultérieurement, mais vous pouvez voir que j'effectuer classique module test en lisant les données de test à partir d'un fichier texte, instancier des deux objets disponibles à partir du module TwoCardPokerLib, appelez la méthode de comparaison et comparez résultats réels avec les résultats attendus pour déterminer les résultats de test étape de Échec.
Figure 1 ad hoc test avec IronPython de la ligne de commande
>>> import sys
>>> sys.path
['C:\\IronPython', 'C:\\IronPython\\Lib']
>>> sys.path.append(r'C:\ModuleTestingWithPython\TwoCardPokerLib\bin\ Debug')
>>> sys.path
['C:\\IronPython', 'C:\\IronPython\\Lib', 'C:\\ModuleTestingWithPython\\ TwoCardPokerLib\\bin\\Debug']
>>>
>>> import clr
>>> dir()
['_', '__builtins__', '__doc__', '__name__', 'clr', 'site', 'sys']
>>>
>>> clr.AddReferenceToFile("TwoCardPokerLib.dll")
>>> from TwoCardPokerLib import *
>>> dir()
['Card', 'Hand', '_', '__builtins__', '__doc__', '__name__', 'clr', 'site', 'sys']
>>>
>>> c1 = Card()
>>> c2 = Card("9d")
>>> print c1,c2
As 9d
>>>
>>> h1 = Hand(c1,c2)
>>> h2 = Hand("Ah","8c")
>>> expected = 1
>>> actual = h1.Compare(h2)
>>>
>>> if actual == expected: print "Pass\n",
... else: print "Fail\n"
...
Pass
>>>
>>> ^Z
C:\IronPython>
La figure 2 test Automation avec un script IronPython
Dans les sections qui suivent, je vais décrire la bibliothèque de classe TwoCardPokerLib conditions de test afin de vous comprendrez exactement ce qui est testé. J'aborderai ensuite les commandes IronPython interactifs, que J'AI utilisé dans la figure 1 . Ensuite je présenter et expliquer en détail le script Python court qui produit la sortie dans la figure 2 . Le code source complet pour le harness test et la bibliothèque en cours de test sont dans le téléchargement qui accompagne cet article.
Je vais conclure avec une brève discussion comment vous pouvez adapter et étendre les idées présentées ici pour répondre à vos propres tests besoins. Je suis certain que vous trouverez que la Python test techniques présentés ici sera une plus grande votre logiciel de tests de compétences.
Le module en test
Maintenant examinons la bibliothèque en cours de test. J'AI décidé de créer une bibliothèque de classe .NET pour le test contenant suffisamment de fonctionnalités pour afficher les types de problèmes que vous êtes confronté lorsque l'exécution réelle module de test dans un environnement de production, mais pas donc bien complexité que les détails de la bibliothèque obscurs le test problèmes. J'AI imaginé une partie hypothétique de deux cartes poker où chaque joueur reçoit deux cartes de quatre, decks standard, 52 carte. Cela conduit naturellement à une conception orientée objet avec une classe de cartes, une classe disponible et une méthode de comparaison pour déterminer lequel des deux objets disponible est mieux. J'AI décidé que chaque disponible deux cartes s'être classé comme un vidage droit (cartes de la même couleur consécutifs), une paire de deux cartes avec le même rang), un vidage (deux cartes de la même couleur), un droit (deux cartes consécutifs), puis As élevé vers le bas sur quatre élevé. Notez que les trois élevé en main n'est pas possible car trois deux est un droit et trois As est élevé As. Même un simple exemple est plutôt intéressant à implémenter. IronPython peut être utilisée pour tester efficacement des bibliothèques .NET indépendamment de la langue implémentation, et IronPython peut être utilisé pour tester les bibliothèques COM classiques. Le code source pour la classe de carte dans ma bibliothèque TwoCardPokerLib est répertorié dans la figure 3 .
Figure 3 la classe de carte de la bibliothèque de test
public class Card
{
private string rank;
private string suit;
public Card() {
this.rank = "A"; // A,2, . . ,9,T,J,Q,K
this.suit = "s"; // c,d,h,s
}
public Card(string s) {
this.rank = s[0].ToString();
this.suit = s[1].ToString();
}
public override string ToString() {
return this.rank + this.suit;
}
public string Rank {
get { return this.rank; }
}
public string Suit {
get { return this.suit; }
}
public static bool Beats(Card c1, Card c2) {
if (c1.rank == "A") {
if (c2.rank != "A") return true;
else return false;
}
else if (c1.rank == "K") {
if (c2.rank == "A" || c2.rank == "K") return false;
else return true;
}
else if (c1.rank == "Q") {
if (c2.rank == "A" || c2.rank == "K" || c2.rank == "Q") return false;
else return true;
}
else if (c1.rank == "J") {
if (c2.rank == "A" || c2.rank == "K" || c2.rank == "Q" || c2.rank == "J")
return false;
else
return true;
}
else if (c1.rank == "T") {
if (c2.rank == "A" || c2.rank == "K" || c2.rank == "Q" ||
c2.rank == "J" || c2.rank == "T")
return false;
else return true;
}
else { // c1.rank is 2, 3, . . 9
int c1Rank = int.Parse(c1.rank);
int c2Rank = int.Parse(c2.rank);
return c1Rank > c2Rank;
}
} // Beats()
public static bool Ties(Card c1, Card c2) {
return (c1.rank == c2.rank);
}
} // class Card
Afin de conserver mon code court et les idées principales effacer, J'AI a eu des raccourcis que vous ne voudriez pas prendre dans un environnement de production, telles que vous avez omis tous les contrôles d'erreur. Ma classe carte a été intentionnellement conçu pour illustrer de nombreuses fonctionnalités courantes d'un module standard de .NET. Notez un constructeur de carte par défaut, qui crée un As de pique carte objet. Il existe un constructeur qui accepte une chaîne telle que TD pour créer un objet qui représente un dix de carte de losange. J'implémenter propriétés get pour exposer le rang et la couleur d'un objet de carte. Et j'implémenter statiques méthodes battements et égalités qui peuvent être utilisées pour déterminer si un objet carte bat ou lie un autre objet carte. Ma classe disponible est trop long pour présenter dans son intégralité, donc je vais décrire uniquement les parties de clé de la classe. La classe disponible a que deux champs de membre :
public class Hand {
private Card card1;
private Card card2;
// constructors, methods, etc.
}
Je vous l'hypothèse important que card1 est le plus élevé de (ou éventuellement égal à) les deux objets carte. Cela simplifie considérablement la logique de ma méthode hand.Compare. Ma classe disponible comporte plusieurs constructeurs, une situation typique, vous devez prendre en compte lors de l'exécution module test. Le constructeur de stock par défaut crée une main avec deux cartes ace–of-Pique :
public Hand() {
this.card1 = new Card();
this.card2 = new Card();
}
J'implémenter deux autres constructeurs disponible me permettent à un passage dans deux objets carte ou les deux chaînes :
public Hand(Card c1, Card c2) {
this.card1 = new Card(c1.Rank + c1.Suit);
this.card2 = new Card(c2.Rank + c2.Suit);
}
public Hand(string s1, string s2) {
this.card1 = new Card(s1);
this.card2 = new Card(s2);
}
Dans la figure 4 j'implémenter quatre méthodes helper privées. Comme vous le verrez, méthodes privées ne sont pas exposés à un script de test IronPython.
La figure 4 privées méthodes
private bool IsPair() { return this.card1.Rank == this.card2.Rank; }
private bool IsFlush() { return this.card1.Suit == this.card2.Suit; }
private bool IsStraight() {
if (this.card1.Rank == "A" && this.card2.Rank == "K") return true;
else if (this.card1.Rank == "K" && this.card2.Rank == "Q") return true;
// etc: Q-J, J-T, T-9, 9-8, 8-7, 7-6, 6-5, 5-4, 4-3, 3-2
else if (this.card1.Rank == "A" && this.card2.Rank == "2") return true;
else return false;
}
private bool IsStraightFlush() { return this.IsStraight() &&
this.IsFlush();
}
Comme toujours un générale règle générale, lorsque exécution module vous test ne teste pas explicitement méthodes privées. L'idée est que toutes les erreurs dans les méthodes d'aide sont exposées lorsque vous testez la méthode publique qui utilise l'application d'assistance. La méthode hand.Compare est étonnamment complexe. J'AI codé privées méthodes helper battements et égalités et les reportez-vous pour implémenter la méthode de comparaison publique :
public int Compare(Hand h) {
if (this.Beats(h))
return 1;
else if (this.Ties(h))
return 0;
else if (h.Beats(this))
return -1;
else
throw new Exception("Illegal path in Compare()");
}
J'utilise le paradigme de fonction strcmp(s,t) langage C ancien pour le paramètre de stock « gauche » (cette » objet) hand.Compare—if bat le paramètre « droite » (le explicite disponible paramètre d'entrée), la comparaison renvoie la valeur 1. Si le paramètre droit ne le paramètre gauche comparaison renvoie-1. Si les deux objets disponible sont équivalents à mes règles, comparaison renvoie 0. L'essentiel du travail est effectuée par la méthode battements privée, qui est illustrée figure 5 .
Figure 5, la méthode Battements
private bool Beats(Hand h) {
if (this.IsStraightFlush()) {
if (!h.IsStraightFlush()) return true;
if (h.IsStraightFlush()) {
if (Card.Beats(this.card1, h.card1)) return true;
else return false;
}
} // this.IsStraightFlush()
else if (this.IsPair())
// code
else if (this.IsFlush())
// code
else if (this.IsStraight())
// code
else
// code for Ace-high down to Four-high
}
return false;
}
Le code pour les battements est sur une longue page et vous pouvez consulter les détails si vous êtes intéressé en examinant le téléchargement de code associé. J'AI inséré délibérément une erreur logique dans la méthode égalités privée :
else if (this.IsFlush() && h.IsFlush() &&
this.card1.Rank == h.card1.Rank) // error
return true;
Si les objets disponibles deux comparées sont les vidages, puis je vérifier si les deux mains avoir le même rang de la carte élevé. Cependant je n'est pas vérifier la deuxième carte de chaque main que J'AI doit :
else if (this.IsFlush() && h.IsFlush() &&
this.card1.Rank == h.card1.Rank &&
this.card2.Rank == h.card2.Rank) // correct
return true;
Cette erreur logique produit l'échec du test illustré figure 2 . J'AI créé ma bibliothèque à l'aide de Visual Studio pour créer un projet de bibliothèque de classes nommé TwoCardPokerLib à TestingWithPython C:\Module, qui se traduit par un fichier TwoCardPokerLib.dll à C:\ModuleTestingWithPython\TwoCardPokerLib\bin\Debug.
Ad hoc interactive module test
Maintenant voyons comment vous pouvez examiner et tester les bibliothèques .NET à l'aide de IronPython. En particulier, examinons chacun des commandes affichées dans la capture d'écran dans la figure 1 . La première partie de la sortie illustrée figure 1 indique que j'utilise la version 1.1.1 de IronPython.
IronPython est un téléchargement gratuit disponible à partir de CodePlex, le projet sponsorisé par Microsoft open source, au codeplex.com/IronPython. Il nécessite le .NET Framework 2.0 et s'exécute sur tout ordinateur qui prend en charge cette version du Framework. J'utilise Windows Vista. Vous n'est pas vraiment installer IronPython, au lieu de cela vous simplement Téléchargez un seul fichier compressé sur votre ordinateur et extrayez son contenu dans un répertoire pratique. Dans mon cas je stockées tous les fichiers IronPython et tous les sous-répertoires à C:\IronPython. J'AI appelé l'interpréteur de ligne de commande Python (ipy.exe) et spécifié un facultatif - X: argument TabCompletion pour permettre Onglet Exécution. Si vous tapez ipy.exe-s-h vous allez obtenir une liste de toutes les options. IronPython au démarrage, il exécute un script de démarrage spécial, appelé site.py, si un tel script existe. J'AI utilisé le fichier site.py standard fourni avec IronPython, sans les modifications.
Une fois que IronPython, j'examinez les informations de chemin d'accès système IronPython en cours :
>>> import sys
>>> sys.path
['C:\\IronPython', 'C:\\IronPython\\Lib']
Tout d'abord je émettez une commande de sys Importation, afin que j'avoir accès aux méthodes dans le module sys IronPython spécial. Ensuite j'affiche la liste des chemins d'accès IronPython examinera lors de la recherche de fichiers qui ne sont pas dans le répertoire en cours. Vous pouvez considérer ceci comme un mécanisme IronPython local un peu similaire à la variable d'environnement PATH système d'exploitation Windows. Étant donné que J'AI appelée IronPython avec la fonctionnalité TabCompletion, si je souhaitais connaître quelles propriétés et méthodes disponibles pour moi à partir du module sys, J'AI peut avez tapé sys. et appuyez sur la touche <tab> à plusieurs reprises. Ensuite j'indiquer IronPython où se trouve mon module .NET cours de test :
>>> sys.path.append(r'C:\ModuleTestingWithPython\TwoCardPokerLib\bin\Debug')
>>> sys.path
['C:\\IronPython', 'C:\\IronPython\\Lib', 'C:\\ModuleTestingWithPython\\TwoCardPokerLib\\bin\\Debug']
J'utilise la méthode path.append du module sys pour ajouter un nouveau répertoire à la liste de recherche IronPython. Notez le caractère r en face de l'argument de chaîne à ajouter. Dans Python vous pouvez utiliser soit seule devis ou des guillemets autour des chaînes. Toutefois, contrairement à certaines langues, Python évaluer des caractères d'échappement tels les caractères \n dans chaînes unique entre guillemets et entre guillemets double. Pour vous assurer qu'une chaîne est interprétée strictement comme littéral (une chaîne « brute » dans la terminologie Python) vous pouvez utiliser le modificateur r comme je l'ai fait ci-dessus. Après avoir ajouté le nouveau répertoire, je vérifie que je n'a pas rendre éventuelles erreurs de frappe en émettant une commande sys.path. Ensuite je préparer charger ma DLL conditions de test à premières méthodes de l'activation peuvent charger les modules de .NET :
>>> import clr
>>> dir()
['_', '__builtins__', '__doc__', '__name__', 'clr', 'site', 'sys']
J'AI émettez une commande de clr (pour le common language runtime) Importation et maintenant j'ont accès sur le module clr, qui possède plusieurs méthodes qui peuvent charger les assemblys .NET. Puis j'utilise le dir() Python commande pour les modules qui je dispose actuellement accès à. Notez que je n'ont pas actuellement accès à la bibliothèque TwoCardPokerLib. Je peux maintenant utiliser le module clr pour accéder à ma bibliothèque en cours de test :
>>> clr.AddReferenceToFile("TwoCardPokerLib.dll")
>>> from TwoCardPokerLib import *
>>> dir()
['Card', 'Hand', '_', '__builtins__', '__doc__', '__name__', 'clr', 'site', 'sys']
J'utilise la méthode AddReferenceToFile pour activer mon environnement IronPython actuel pour l'appeler la DLL TwoCardPokerLib. Python est sensible à la casse, donc si je l'aviez tapée addreferencetofile, par exemple, J'AI ne serait ont reçu une erreur-par-attribut (méthode).
Après avoir ajouté une référence à mon module cours de test, je dois utiliser l'instruction d'importation pour rendre le module disponibles. Je peut dispose Importation tapée TwoCardPokerLib mais je tape dans l'importation TwoCardPokerLib * au lieu de cela. Si j'utilise le formulaire tout d'abord, plus simple, J'AI faudrait qualifier entièrement tout dans mon module, par exemple, TwoCardPokerLb.hand au lieu de simplement taper disponible. La forme de-<module>-Importation-<classes> de la commande d'importation m'omettre le nom du module parent lorsque je tape le permet. Notez qu'après une commande dir() faire, J'AI que les classes de carte et disponible dans le module TwoCardPokerLib sont maintenant disponibles pour moi. Maintenant je peuvent exercer mon module cours de test en créant deux objets de carte :
>>> c1 = Card()
>>> c2 = Card("9d")
>>> print c1,c2
As 9d
J'utilise le constructeur de carte par défaut pour instancier un objet nommé c1. N'oubliez pas de la section précédente, le constructeur de carte par défaut crée un As de pique objet. J'utilise le constructeur de carte par défaut pour créer un objet représentant un neuf des losanges. Remarquez que je n'utilisent pas un mot clé comme « nouveau » pour instancier des objets comme je le feriez dans certaines langues. Si J'AI a utilisé la court "Importation TwoCardPokerLib « déclaration antérieure de, J'AI serait avez appelé le constructeur en tant que « c1 = TwoCardPokerLib.Card() ». J'AI utilisez l'intrinsèque instruction d'impression Python pour afficher mes deux objets carte. En coulisses, l'instruction impression IronPython est appelant en fait la méthode de Card.ToString() que J'AI implémenté dans ma bibliothèque de classe. Maintenant j'instancier des deux objets disponibles et appeler la méthode hand.Compare :
>>> h1 = Hand(c1,c2)
>>> h2 = Hand("Ah","8c")
>>> expected = 1
>>> actual = h1.Compare(h2)
Je transmets les deux objets carte, que J'AI créé simplement dans un constructeur disponible pour créer une première main, et puis J'AI utiliser la version paramètre de chaîne du constructeur pour instancier une main seconde. Étant donné qu'en main D'as-neuf bat en main D'as-huit, prévu la méthode de comparaison pour renvoyer 1 donc je stocke cette valeur dans une variable appelée attendue. Notez que Python est un langage dynamiquement typé, donc je ne déclarer des types de données. J'AI appeler le hand.Compare méthode et banque le résultat dans une variable nommée réel. Je peux afficher les méthodes disponibles dans la classe disponible en tapant disponible. sur la ligne de commande et puis cliquant sur la touche <tab>. Les méthodes publics telles que comparer et ToString affiche-t-elle mais méthodes privées comme battements et égalités pas serait apparaît. Maintenant je peux déterminer un résultat étape de Échec pour mon test interactif ad hoc :
>>> if actual == expected: print "Pass\n",
... else: print "Fail\n"
...
Pass
>>>
La syntaxe de Si-alors Python sur une ligne de commande est un peu inhabituelle, donc je vais l'expliquer dans la section suivante. Mais essentiellement je vérifier pour vérifier si la valeur de la variable appelée réelle est égale à la valeur dans la variable appelée prévu. Si c'est le cas, j'affiche un message « phase »; sinon, j'imprimer « Échec. Notez que j'ai inclus un caractère de nouvelle ligne dans chaque chaîne. La intermédiaire «... » réponse de l'interpréteur de IronPython indique que je dispose d'une commande incomplète et que l'interpréteur attend à exécuter la commande.
Léger module test Automation
Maintenant nous allons voir comment écrire des scripts IronPython légères pour tester les bibliothèques de classes .NET. Le script dans la figure 6 produit le résultat illustré figure 2 .
La figure 6 fichiers harness.py
# harness.py
# test TwoCardPokerLib.dll using data in TestCases.txt
print "\nBegin test run\n"
import sys
print "Adding location of TwoCardPokerLib.dll to sys.path"
sys.path.append(r'C:\ModuleTestingWithPython\TwoCardPokerLib\bin\Debug')
import clr
print "Loading TwoCardPokerLib.dll\n"
clr.AddReferenceToFile("TwoCardPokerLib.dll")
from TwoCardPokerLib import *
print "=================================="
fin = open("TestCases.txt", "r")
for line in fin:
if line.startswith("$"):
continue
(caseID,input,method,expected,comment) = line.split(':')
expected = int(expected)
(left,right) = input.split(',')
h1 = Hand(left[0:2],left[2:4])
h2 = Hand(right[0:2],right[2:4])
print "Case ID = " + caseID
print "Hand1 is " + h1.ToString() + " " + "Hand2 is " + h2.ToString()
print "Method = " + method + "()"
actual = h1.Compare(h2)
print "Expected = " + str(expected) + " " + "Actual = " + str(actual)
if actual == expected:
print "Pass"
else:
print "** FAIL **"
print "=================================="
fin.close()
print "\nEnd test run"
# end script
Début de mon script IronPython test harness avec commentaires :
# harness.py
# test TwoCardPokerLib.dll using data in TestCases.txt
Le # est le caractère commentaire pour les scripts Python. Python est ligne en fonction, de sorte que commentaires exécuter via la fin d'une ligne. Python prend également en charge des commentaires sur plusieurs lignes à une seule triple-devis. Python scripts utilisent une extension de fichier .PY et peuvent être créées à l'aide n'importe quel éditeur de texte, y compris le bloc-notes. Mon harness test lit des données cas de test à partir d'un fichier externe nommé TestCases.txt :
0001:AcKc,AdAs:Compare:1: "royal flush" > pair Aces
0002:Td9d,Th9h:Compare:0: straight flush diamonds == straight flush hearts
$0003:Ah2c,As9c:Compare:1: straight > Ace high
0004:9h6h,9d7d:Compare:-1: flush 9-6 high < flush 9-7
0005:KcJh,As5d:Compare:-1: King high < Ace high
Ici, j'ai seulement cinq scénarios de test, mais dans un scénario de production serait j'ai des centaines, voire des milliers. Il existe 7,311,616 ou 524 entrées légales à hand.Compare, qui illustre le impracticality de tests exhaustively modules même simples.
Chaque ligne représente un seul cas de test. Chaque champ est délimité par le caractère deux-points. Le premier champ est un code de test. Le deuxième champ est une chaîne qui contient des informations pour deux objets disponible, séparés par un caractère de la virgule. Le troisième champ indique quelle méthode à tester. Le quatrième champ est la valeur attendue. Le cinquième champ est facultatif et est un commentaire de cas de test. Notez test 0003 est précédée par un caractère $. J'analyse pour ce caractère dans mon harness test et ignorer les ces tests. Test 0004 génère un résultat échouent en raison de l'erreur (uniquement fins de démonstration) logique délibérée J'AI placé dans la méthode hand.ties qui est appelée par la méthode hand.Compare conditions de test. Stocker les données de test dans un fichier texte est rapide et facile. J'AI peut également ont incorporées mes données de test directement dans mon harness, ou stockées mon cas dans un fichier XML et ainsi de suite. Python peut gérer facilement un modèle de stockage de cas de test. Ensuite, j'afficher un message à l'interface de commande :
print "\nBegin test run\n"
Étant donné que Python chaînes peuvent être délimitées par doubles guillemets ou apostrophes et les caractères d'échappement sont exprimées dans les deux styles de devis, le choix entre les types de devis est vraiment une question de préférence. Je généralement utiliser guillemets sauf lorsque je vais utiliser le modificateur r pour rendre ma chaîne un littéral, auquel cas j'ont tendance à utiliser des guillemets simples. Ensuite mon script de harness de test Python ajoute l'emplacement de ma DLL conditions de test au chemin d'accès système Python :
import sys
print "Adding location of TwoCardPokerLib.dll to sys.path"
sys.path.append(r'C:\ModuleTestingWithPython\TwoCardPokerLib\bin\Debug')
Ici, J'AI coder en dur l'emplacement de la DLL conditions de test. Je peux passer des arguments de ligne de commande à mon script python et y accéder avec le tableau sys.argv intégré. Par exemple, si je devais à appeler mon script en tant que
> ipy.exe harness.py C:\Data MyLib.dll
puis sys.argv[0] contient le nom de mon script harness.py, sys.argv[1] serait contenir le premier argument (C:\Data) et sys.argv[2] contient le deuxième argument (MyLib.dll). Ensuite savoir Mes harness charger mon module en cours de test :
import clr
print "Loading TwoCardPokerLib.dll\n"
clr.AddReferenceToFile("TwoCardPokerLib.dll")
from TwoCardPokerLib import *
Il existe plusieurs façons pour charger une bibliothèque .NET-based utilisant IronPython. La méthode clr.AddReferenceToFile est simple et efficace lorsque votre sys.path contient l'emplacement de la bibliothèque à ajouter, mais le module clr contient également des alternatives notamment clr.AddReference, clr.AddReferenceByName, clr.AddReferenceByPartialName et clr.AddReferenceToFileAndPath. Ici, j'utilise le * générique pour charger toutes les classes à partir de la bibliothèque TwoCardPokerLib, mais je peut charger des classes spécifiques par leur nom. Python propose plusieurs manières pour traiter un texte fichier ligne par ligne. J'AI utiliser la fonction Ouvrir fichier intrinsèque et que vous il transmettre le nom de mon fichier de test et un argument r pour indiquer que je suis ouvrir le fichier pour la lecture :
print "=================================="
fin = open("TestCases.txt", "r")
for line in fin:
# process line here
Autres modes courants incluent w pour écriture et d'ajout. L'argument mode est facultative et par défaut r afin que je peut ont laissé des. Vous pouvez trouver une bonne référence pour fonctionnalités de langage Python, syntaxe et des fonctions intrinsèques dans docs.python.org. Le résultat de la fonction open est un descripteur de fichier que j'affecter à une variable nommée fin. Puis j'utiliser une boucle effectuer une itération ligne par ligne. J'AI nommés ma ligne-stockage variable « ligne », mais j'aurais pu utiliser n'importe quel nom de variable légal. Notez un truc syntaxique de Python : au lieu d'utiliser jetons de début-fin comme {. .. } ou commencez. .. fin comme la plupart des langages ne, Python utilise le caractère deux-points conjointement avec la mise en retrait pour indiquer le début et de fin de blocs d'instruction. Dans mon boucle de traitement principal, j'utilise la fonction de chaîne startswith intrinsèque pour vérifier si la ligne active de mon fichier test commence par un caractère dollar :
if line.startswith("$"):
continue
Si je trouve un $, j'utiliser l'instruction continue pour ignorer les instructions restantes dans la de boucle et lecture de la ligne suivante à partir de mon fichier de test. Python possède un ensemble complet de boucle et décision structures de contrôle. Ensuite j'analyser ma ligne de données test dans ses champs individuels :
(caseID,input,method,expected,comment) = line.split(':')
expected = int(expected)
J'utilise une fonctionnalité intéressante de Python appelé un tuple et la fonction de chaîne fractionnement intrinsèque pour analyser rapidement les données. J'AI peut avez effectué la tâche en utilisant une approche de tableau traditionnel que vous consultez ci-dessous, mais je pense que l'approche de tuple Python est à la fois plus court et plus facile à comprendre.
tokens = line.split(':')
caseID = tokens[0]
input = tokens[1]
method = tokens[2]
expected = tokens[3]
comment = tokens[4]
J'utilise la fonction int() pour convertir explicitement la variable appelée attendue de type chaîne à taper int de sorte que je peut comparer prévu avec réel. Autres fonctions de conversion de type utile incluent str(), float(), long() et bool(). Ensuite j'effectue une deuxième analyse pour extraire les deux mains entrées :
(left,right) = input.split(',')
h1 = Hand(left[0:2],left[2:4])
h2 = Hand(right[0:2],right[2:4])
Après ma première analyse avec double affichage, l'entrée variable contient une valeur de chaîne comme KcJh, As5d. Par conséquent, j'appelle fractionner à nouveau avec un, argument et stocker les deux résultats dans les variables que J'AI appelé gauche et droite. Maintenant, la variable chaîne appelée gauche contient KcJh et droite contient As5d. Contrairement à nombreux langages, Python n'a pas une méthode de sous-chaînes. Au lieu de cela, Python utilise l'indexation de tableau pour extraire des sous-chaînes. Tableau de l'indexation commence à 0, donc si la variable de gauche contient KcJh l'expression gauche [0: 2] produit kc et l'expression gauche rendements [2:4] Jh. Notez que pour extraire une sous-chaîne d'une chaîne de plus, vous spécifier l'index du premier caractère dans la chaîne de plus grande (comme vous pouvez l'imaginer) mais une plus l'index de caractère de fin. Une fois que J'AI avez chaînes qui représentent des cartes individuelles, je transmets les au constructeur disponible. Ensuite j'écho mes données d'entrée à l'interpréteur de commandes :
print "Case ID = " + caseID
print "Hand1 is " + h1.ToString() + " " + "Hand2 is " + h2.ToString()
print "Method = " + method + "()"
Python utilise le + le caractère à exécution concaténation de chaînes, de sorte que dans les trois instructions impression ci-dessus je suis imprimer trois chaînes. La fonction impression accepte plusieurs arguments séparés par, donc j'ai a écrit instructions comme suit pour produire le même résultat :
print "Case ID = " , caseID
Je suis désormais prêt à appeler la méthode conditions de test :
actual = h1.Compare(h2)
print "Expected = " + str(expected) + " " + "Actual = " + str(actual)
Notez que car J'AI implémenté la méthode hand.Compare comme une méthode d'instance, j'appelle il à partir du contexte de l'objet disponible h1. Si J'AI eu codé comparaison comme une méthode statique je serait ont appelée comme suit :
actual = Compare(h1,h2)
Notez que ce stade les variables réels et attendues sont de type int car réel est la valeur renvoyée par la méthode de comparaison qui est définie pour renvoyer un int et prévue a été explicitement castée en un Int. Donc je cast réel et attendu en chaînes afin que je pouvez concaténer une chaîne de sortie unique. Maintenant j'afficher mon résultat de étape de Échec test :
if actual == expected:
print "Pass"
else:
print "** FAIL **"
print "=================================="
Python utilise retrait pour indiquer le début et la fin de bloc d'instructions. Si vous avez beaucoup d'expérience de programmation dans d'autres langues, Utilisation de Python de retrait peut sembler un peu étrange au premier abord. Mais la plupart des testeurs j'avez parlais à dire que problèmes inhabituels syntaxe du Python familiariser très rapidement.
Si je souhaite effectuer le suivi du nombre total de tests qui transmettent, J'AI pouvez initialiser les compteurs en dehors de mon boucle de traitement principal comme suit :
numPass = numFail = 0
puis modifier ma structure Si-alors :
if actual == expected:
numPass += 1
print "Pass"
else:
numFail += 1
print "** FAIL **"
Puis je peut imprimer mes résultats en dehors de la boucle de traitement :
print "Num pass = " , numPass , " num fail = " , numFail
Notez que Python ne prend pas en charge la syntaxe comme numPass ++ ou ++ numPass pour incrémenter une variable int. Ici, je suis suffisait d'imprimer résultats à l'interface de commande, mais je peux facilement écrire des résultats à un fichier texte, fichier XML, base de données SQL ou autre stockage. Terminer maintenant mon harness test haut :
fin.close()
print "\nEnd test run"
# end script
JE fermer mon référence de fichier test pour libérer le descripteur de fichier et imprimer un message indiquant que le test est terminée. Structure de mon script est très simple mais Python possède des fonctionnalités qui vous permettent de gérer les scripts complexes. Par exemple, dans un environnement de production I est sans aucun doute ajuster mon script entier dans un gestionnaire d'exceptions :
try
# harness code here
except
# handle any exceptions here
En outre, Python prend en charge fonctions défini pour le programmeur, donc j'ont pourrait structuré mon harness comme une fonction principale plus de fonctions d'assistance.
Conclusion
Module automatisée test est sans doute le type plus fondamental d'automatisation de test logiciel. Chaque fois que J'AI voulez évaluer la configuration d'un langage de programmation pour l'automatisation de test, je vérifie tout d'abord comment la langue traite module test. À mon avis, IronPython transmet ce test litmus avec flying colors. Langage de programmation est parfaite pour toutes les tâches et les scénarios de test. Ceci dit, IronPython possède de nombreuses fonctionnalités qui en font un choix excellent comme module langue test.
Me laisser fin à mentionner comment léger module test automation avec IronPython est lié aux tests d'unités. Tests d'unité avec les infrastructures tels que NUnit sont plus souvent placés directement dans votre code de module. Développement test pilotées par l'avec test d'unité est principalement une activité de développeur. L'utilisation d'une approche de test unité lors du développement ne pas absolve vous à partir de la responsabilité d'exécution détaillée, dédié module test. C'est où Python entre dans. En d'autres termes, module de test avec Python est un complément pour pas substituer, test d'unité. Lorsque les deux approches sont utilisés ensemble, vous pouvez créer logiciels mieux, plus fiable.
Envoyez vos questions et commentaires à James à testrun@microsoft.com.
Dr. James McCaffrey travaille pour Volt Information Sciences, Inc., où il gère les formations techniques pour les ingénieurs logiciels chez Microsoft. Il a travaillé sur plusieurs produits Microsoft, dont Internet Explorer et MSN Search. James est l'auteur de .NET Test Automation Recipes. Vous pouvez le contacter à l'adresse jmccaffrey@volt.com ou v-jammc@microsoft.com.