| Mettre en place son environnement de développement .NET par Laurent Desmons (ldesmonsmcsd@hotmail.com) | ||
Aucun doute, les IDE actuels comme Visual Studio .NET sont des outils formidables. Toutefois, dès qu'il s'agit de vouloir faire du développement sérieux en équipe, il est nécessaire de les compléter en utilisant au quotidien des outils bien pratiques voire indispensables, qui plus est totalement gratuits.
Cet article se propose donc de guider le lecteur à travers la mise en oeuvre d'un environnement de développement rigoureux, stable et assez souple pour pouvoir être utilisé lors de projets de plus ou moins grande envergure.
Tout chef de projet qui se respecte devrait ainsi songer à intégrer les outils suivants dans le cycle de développement des projets dont il a la charge :
Ces outils sont tous complémentaires. Libre au lecteur de choisir tel ou tel outil pour chacune de ces catégories. Dans cet article je présenterai les outils que j'utilise personnellement au quotidien, les motivations qui me poussent à les utiliser, ainsi que leur mise en oeuvre détaillée, tout cela au travers d'un exemple très simple. L'intégration de ces outils sera progressive.
J'espère ainsi démontrer au lecteur que l'investissement de départ nécessaire à la domestication de ces outils n'est rien du tout comparé aux bénéfices qu'ils apportent tout au long de la réalisation du projet.
Pour les besoins de cet article je vais me baser sur une solution Visual Studio .NET appelée "NetEnvDev" comportant les projets C# suivants :
une application Windows Forms appelée MainForm, avec une seule forme appelée MainForm.
une bibliothèque de classes appelée MainLib, avec une seule classe appelée MainClass. Cette classe expose une propriété Version, ainsi qu'une méthode Add() très simple effectuant l'addition de deux entiers et renvoyant le résultat du calcul
Le principe de cette application est le suivant :
MainForm instancie un objet de type MainClass
MainForm récupère la propriété Version de l'objet MainClass ainsi créé, et affiche le résultat (ex:"Version 1.0") dans un label.

|
Simplissime et inutile, n'est-ce-pas ?? C'est le but recherché, afin de ne pas nous encombrer de code applicatif et de nous concentrer sur les détails d'intégration de chaque outil.
Cette solution est prise en charge par plusieurs développeurs qui travaillent sur des machines différentes. Conformément aux bonnes pratiques en vigueur, un serveur séparé fait office de machine de build de référence.
Même pour ce projet minuscule, une première remarque s'impose alors : pour éviter tout problème il est fortement recommandé d'adopter la même structure de répertoires sur chaque poste de développement ainsi que sur le serveur de build, aussi bien pour les sources applicatives que pour les assembly externes dont dépend chaque projet. En effet, Visual Studio .NET stocke les références des assembly locales sous la forme d'un chemin relatif dans le fichier du projet, dans la section <references> et l'attribut HintPath. Vous n'aurez pas de soucis si vous n'avez que des références par projet, mais dès que vous ajoutez des références par fichier sur une assembly externe, le chemin d'accès est relatif au projet, et risque donc de ne plus correspondre sur une autre machine, si vous n'avez pas adopté une structure de répertoires identique.
Comme chaque développeur partage le même code source de référence, il est indispensable de mettre en oeuvre des outils de contrôle de ce code source, dès le début du projet, afin d'éviter tout conflit potentiel et de gérer les versions (bien sûr, pour les toutes petites équipes de développement, on peut s'en passer, mais au-delà de 2 développeurs on peut difficilement se permettre d'aller au devant de grandes difficultés de maintenance et de souplesse au quotidien).
Notez bien au passage que cela ne se réduit pas au code source : on peut également contrôler les versions de la documentation, les fichiers de configuration, les fichiers de projet pour les outils, le fichiers de clés ".snk" pour signer les assembly avec des noms forts, les scripts SQL pour les bases de données, etc...
Le choix le plus immédiat se porte naturellement vers Visual SourceSafe 6.0d de Microsoft, car il est livré avec Visual Studio .NET. Cet outil a des défauts (le pire de tous étant sans doute sa mauvaise adaptation à Internet et ses performances parfois nettement dégradées avec la taille des projets) mais il est facile d'emploi et de mise en oeuvre, et intimement intégré à Visual Studio .NET. Remarque importante: pour des raisons qui apparaîtront claires plus tard, il faut utiliser la version anglaise de VSS et non pas la version française.......
Une alternative fiable et éprouvée consiste à utiliser un serveur CVS dans sa version Windows (CVSNT) et à intégrer un client CVS à VS .NET en passant par un plug-in SCC comme Igloo ou (bien mieux) le CVS SCC proxy plug-in de PushOK. On peut également faire appel à Perforce, qui est peut-être ce qui se fait de mieux en la matière, mais qui n'est pas gratuit.
L'utilisation d'un outil de contrôle de code source peut se résumer à des principes assez simples :
L'outil est client/serveur : il maintient le code source de référence dans une base de données unique, centralisée, distante, accessible sur un réseau local ou par Internet par des postes clients
Chaque poste client possède une copie locale des sources de ce référentiel (en lecture seule). L'outil permet alors d'extraire ("check out") un ou plusieurs fichiers depuis la base de données distante pour modification - par défaut, avec VSS, le fichier est alors ouvert en exclusif, ce qui empêche d'autres développeurs de le modifier. CVS, quand à lui, gère les accès concurrents en prenant en charge les différences et en prévenant de tout conflit éventuel
Typiquement, le matin en arrivant au travail, le développeur ouvre sa propre solution locale (extraite, à l'origine, depuis la base distante). Il peut alors tranquillement apporter ses modifications, qui ne sont pas visibles des autres postes, et notamment du serveur de build central (c'est tout l'intérêt). Une fois les modifications apportées, le développeur doit alors mettre à jour la base de données centrale (on parle alors "d'archiver le fichier", ou "check in") afin de libérer le fichier pour les autres développeurs et valider ses modifications (qui ont alors tout intérêt à être bien testées et éprouvées localement, nous y reviendrons)
L'outil maintient toutes les versions des fichiers (CVS ne maintient que les différences, ce qui explique qu'il soit plus performant et mieux adapté pour les gros projets). On peut consulter l'historique des modifications, associer des commentaires à ces modifications, revenir en arrière sur n'importe quelle version...
Ces principes sont valables quel que soit l'outil de contrôle de code source. L'intérêt de VSS ici est que toutes ces opérations peuvent être effectuées directement depuis Visual Studio .NET, avec un simple clic de souris (en pratique, il est même vivement conseillé de ne pas se servir de l'interface graphique livrée avec Visual SourceSafe pour ces opérations liées aux fichiers d'une solution).
On voit tout de suite que l'utilisation d'un tel outil réclame une bonne dose de discipline de la part des développeurs, du moins au début. Ensuite, l'habitude est vite prise, à comparer aux sources locales maintenues manuellement et mises à jour sur un répertoire partagé entre plusieurs utilisateurs à grands coups hasardeux de WinDiff...
Dans la mesure du possible, il est recommandé de :
maintenir la base de données du contrôleur de source sur une machine séparée et entièrement dédiée
effectuer des sauvegardes régulières de cette base de données (par une simple copie de tous les fichiers du référentiel)
pour VSS, analyser régulièrement cette base de données avec l'utilitaire "Analyze" livré avec VSS
créer des utilisateurs spécifiques dédiés (voire également un utilisateur pour le build général), de façon à protéger l'accès au référentiel à tout utilisateur non autorisé. L'ouverture dans Visual Studio .NET d'une solution contrôlée par VSS s'accompagne alors de l'ouverture d'une boîte de dialogue destinée à identifier l'utilisateur auprès de VSS.
Dans notre cas, pour faire simple, le serveur de build fait également office de serveur de base VSS et maintient donc les sources de référence.
Il est très facile de mettre en oeuvre VSS ici (rappelons qu'on doit utiliser la version en anglais!) :
Installer la partie "serveur de base de données VSS partagée" sur le serveur de build (normalement Visual Studio .NET n'est pas installé sur ce serveur, uniquement le SDK .NET)
Installer la partie "client VSS" sur chaque poste de développement en exécutant NetSetup.exe depuis le répertoire d'installation de VSS sur le serveur
Créer dans un répertoire du serveur de build (par exemple VSS) une nouvelle base de données VSS appelée "NetEnvDev". Avec VSS ce répertoire doit être partagé sur le réseau afin que les autres postes puissent accéder à la base de données VSS
Sur un poste de développement, créer en local avec Visual Studio .NET la solution "NetEnvDev" initiale
Archiver la solution complète dans VSS en sélectionnant l'option "Fichier/Contrôle de code source/Ajouter la solution au contrôle de code source" et en nommant le projet "NetEnvDev" dans VSS
Ensuite, sur chaque poste de développement, extraire initialement l'ensemble de la solution à partir de VSS en sélectionnant l'option "Fichier/Contrôle de code source/Ouvrir à partir du contrôle de code source". Les fichiers sont alors copiés localement et, ensuite, le développeur n'a plus qu'à ouvrir cette solution locale pour travailler.
Afin d'expérimenter ces manipulations, je vous conseille d'utiliser la solution NetEnvDev et de faire en sorte que chaque développeur, à tour de rôle, modifie la classe MainClass pour faire varier le numéro de version affichée dans la forme MainForm : ce n'est que lorsque le développeur qui a extrait le fichier de la classe archive son travail dans le référentiel que la nouvelle version est effectivement mise à disposition des autres développeurs
La procédure est la même pour les projets plus importants. Il faut simplement noter que dans le cas où on a affaire à des applications Web, il est recommandé que :
chaque développeur dispose de son propre site Web local (pour éviter tout conflit ou interlock en cas de déboguage en commun)
les fichiers du site Web ne soient pas directement disposés sous C:\inetpub\wwwroot, mais dans un sous-répertoire de la solution sur lequel pointe un répertoire virtuel IIS correspondant, afin qu'ils puissent facilement être intégrés dans l'arborescence VSS
A ce stade, il faut remarquer deux choses :
d'abord, les fichiers contrôlés par VSS sont affichés dans l'Explorateur de Solutions de Visual Studio .NET accompagné d'un petit cadenas bleu; si j'essaye de modifier un fichier ainsi protégé, Visual Studio .NET me prévient que je vais devoir l'extraire de VSS et me propose de saisir un commentaire. Les fichiers ainsi extraits sont affichés avec un point d'exclamation ou une coche rouge. Par défaut avec VSS, je ne peux ni modifier ni extraire un fichier marqué ainsi et qui n'a pas été extrait par moi (il est d'ailleurs souvent préférable de laisser cette option ainsi, mais cela dépend pour partie de la taille du projet et du nombre de développeurs) :
|
Ensuite, dans l'interface graphique "Visual SourceSafe Explorer" livrée avec VSS, les fichiers contrôlés sont présentés dans un sous-répertoire "NetEnvDev" (le nom de la solution) du répertoire "$/NetEnvDev" (le nom du projet dans VSS). Certains préfèreront alors peut-être laisser le nom initial "NetEnvDev.root" proposé par VSS lors de la création du projet, pour ne pas confondre avec la solution :
|
Dans Visual Studio .NET, il est possible d'archiver chaque fichier extrait individuellement depuis l'Explorateur de Solutions (en y apposant également un commentaire), ou bien d'utiliser la fenêtre "Archivages en attente" qui permet à la fois d'avoir une vision sur l'ensemble des fichiers extraits et également de tous les archiver d'un coup.
Au quotidien, il n'est d'ailleurs pas vraiment conseillé de laisser les fichiers en attente d'archivage trop longtemps, d'autant plus que cela va à l'encontre des principes dits de "l'intégration continue", que je présenterai en fin d'article:
|
Pour un travail de développement en équipe, les bénéfices de ce genre d'outil sont immédiats (et durables) et il me semble donc inutile d'insister plus avant. Pour une vision plus globale et approfondie on se reportera à l'excellent article suivant: Team development with Visual Studio .NET and Visual SourceSafe.
Maintenant que nous sommes dans le code source, attardons-nous donc un peu sur les outils qui permettent de l'écrire un peu plus proprement.
N'importe quel développeur .NET sait écrire du code C#, mais pas forcément du code sécurisé, performant, facilement maintenable, extensible, en harmonie avec les autres développeurs, avec des règles de nommage uniformes et strictes, la prise en compte de la culture, etc.. . le chef de projet doit savoir imposer des règles de développement de façon à ce que son projet soit le plus homogène possible.
Au niveau des projets, répertoires, architectures, Visual Studio .NET propose les Enterprise Templates (sur lesquels je ne reviendrai pas, car ils forment le sujet d'autres articles par ailleurs, comme par exemple celui-ci). En matière de code source, je propose l'outil gratuit FxCop.
Sur le principe, rien de bien compliqué : FxCop se base sur un ensemble de règles pour analyser le code source d'un EXE ou d'une DLL. En cas de violation, l'outil précise la règle, la violation correspondante dans le code source, ainsi que les moyens exhaustifs d'y remédier (exemples à l'appui). Il est bien entendu possible de désactiver une règle en particulier, ou tout un ensemble de règles d'un coup.
Ces règles sont regroupées en ensembles fonctionnels (des assembly) à la manière de plug-ins : règles de nommage, de design, de globalisation, d'usage.... actuellement FxCop prend en charge plusieurs centaines de règles, classées selon différents niveaux de gravité. Les violations les plus graves ne devraient jamais être ignorées.
L'intérêt d'un tel outil ? Hormis quelques considérations qui devraient apparaitre évidentes (nommer les variables en accord avec les recommandations usuelles, adopter de bonnes attitudes et de bons réflexes de design), je considère que l'intérêt principal d'un tel outil est d'attirer l'attention du développeur sur telle ou telle pratique reconnue comme "bonne", afin que non seulement il y soit sensibilisé, et ne prenne donc pas le risque de l'oublier (ou, mieux, prenne l'habitude de l'adopter!) mais que également il soit capable de justifier pourquoi une règle a été volontairement violée ou ignorée, et ce en toute connaissance de cause.
Il s'agit donc bien de responsabiliser l'auteur du code source, et non pas de le "policer". Il faut faire en sorte que l'outil se plie aux besoins du code source, et pas l'inverse.
Il est bien évident qu'il est ensuite entièrement de la responsabilité du chef de projet de décider d'adopter telle ou telle règle, et d'en ignorer d'autres. Mais il faut bien se rendre compte que ces règles sont le plus souvent fondées sur de bons principes, par ailleurs éprouvés. Pour ma part, en utilisant cet outil je cherche avant tout à éviter les erreurs les plus graves, et à comprendre le pourquoi de certaines erreurs plus bénignes. L'expérience en ce domaine montre qu'avec l'habitude il devient beaucoup plus facile de ne plus commettre beaucoup de violations de règles. Il s'agit donc bien à terme d'un moyen comme un autre de mieux maîtriser le Framework .NET.
FxCop se présente sous deux formes différentes : une interface graphique appelée "FxCop" et un utilitaire en mode console appelé "FxCopCmd". Tous deux offrent les mêmes fonctionnalités. Sur les postes de développement nous allons utiliser FxCop; nous reviendrons sur FxCopCmd plus tard, quand nous aborderons le serveur de build.
Pour mettre en oeuvre FxCop dans notre exemple :
Installer FxCop sur chaque poste de développement
Lancer FxCop puis créer un nouveau projet appelé "NetEnvDev".
Ajouter au projet tous les EXE et toutes les DLL (en Release) à analyser (hormis le SetUp): MainForm.exe, MainLib.dll:
|
Sauvegarder le fichier au même niveau que le fichier de la solution, sous le nom "NetEnvDev.FxCop"
Intégrer ce fichier NetEnvDev.FxCop au contrôle de code source à l'aide de VSS Explorer (au même endroit que la solution), de façon à ce que ce fichier puisse être rapatrié facilement sur l'ensemble des postes de développement ainsi que sur le serveur de build - mais attention cependant car à partir de maintenant il faut extraire, modifier et archiver ce fichier depuis VSS Explorer seulement, et pas depuis FxCop, étant donné qu'il est marqué en lecture seule dans le répertoire local, comme n'importe quel autre fichier contrôlé par VSS et qui n'a pas été extrait pour modification - cela dit, quand on cherche à le modifier depuis VSS Explorer, alors celui-ci nous lance bien entendu FxCop après avoir extrait le fichier en local.
|
Ensuite, lancer l'analyse. Voici par exemple ce que donne la première analyse après la première génération de la solution (vous noterez en passant que le code généré par Visual Studio .NET n'est pas tout à fait propre puisque nous n'avons pratiquement rien écrit et qu'il y a déjà des erreurs ;-) :
|
Concrètement maintenant, le travail consiste à éliminer le plus possible de violations, et à lancer l'analyse à chaque génération de code. Le résultat peut être sauvegardé sous la forme d'un fichier au format XML "FxCop Report". Notez toutefois que FxCop (en anglais) peut détecter à tort une erreur liée au dictionnaire associé à certaines règles dites de "spelling" que nous autres Français pouvons ignorer sans risques; ces règles se trouvent dans le package "UsageRules.dll" et sont toutes celles qui sont relatives au dictionnaire (ex : "Member names should consist of correctly spelled words").
Pour ma part, dans notre exemple, après corrections il ne me reste plus que le Warning :
|
Remarques importantes :
il peut arriver que FxCop verrouille les assembly qu'il analyse, bloquant ainsi leur génération dans Visual Studio .NET. Pensez donc à quitter FxCop entre deux analyses.
les DLL à analyser sont spécifiées dans le fichier de projet .FxCop avec leur chemin complet : le répertoire ainsi que la configuration (debug/release) doivent être les mêmes sur tous les postes (et surtout sur le serveur de build!) si on veut pouvoir utiliser ce fichier de projet
afin de pouvoir supprimer l'erreur "Assemblies have strong name", les assembly ont été signées avec un nom fort en utilisant un fichier de clés "NetEnvDev.snk" généré par l'utilitaire "sn.exe" et j'ai spécifié ce fichier dans la directive [AssemblyKeyFile] de chaque fichier assemblyInfo.cs; j'ai disposé ce fichier dans le répertoire de la solution, et, ensuite, je l'ai également ajouté au contrôle de code source afin que chaque développeur puisse en disposer (et, notamment, le serveur de build).
Soyons clairs d'emblée : je veux parler ici uniquement de la documentation du code source. Certains trouveront sans doute étrange que je mentionne la documentation à ce stade. On pourrait s'attendre à ce qu'elle soit générée tout à la fin du cycle de vie d'un projet... il s'agit là d'une erreur bien lourde, sans cesse répétée, et motivée par des raisons souvent révélatrices de problèmes plus graves par ailleurs ("je n'ai pas le temps maintenant", "le code change tout le temps, la documentation aussi", "je ne connais pas encore les méthodes ou l'interface de cette classe, alors la documentation hein...", etc...).
C'est en effet oublier bien vite que plus le code source est documenté tôt, et plus il sera proprement écrit, réfléchi, solide (et plus les tests unitaires seront également bien écrits, mais je reviendrai sur ce point au chapitre suivant). Une bonne documentation prend du temps à être rédigée, c'est vrai, mais bien plus de temps encore en fin de projet qu'au début. Il s'agit encore et toujours de réflexes à acquérir, ce qui passe par une bonne discipline initiale. Ensuite, on n'y pense pratiquement plus et on l'écrit machinalement au moment de la création de la classe ou de ses méthodes ou propriétés (surtout si Visual Studio .NET répète inlassablement les mêmes genres de warnings à chaque compilation !). C'est d'autant plus vrai que normalement les classes et leurs attributs ou méthodes sont déjà spécifiées par ailleurs (par exemple dans des diagrammes UML) et qu'il est donc fort probable que la documentation de code source soit d'emblée (presque) définitive.
Hormis cela, l'intérêt de bien documenter en même temps qu'on rédige le code source est triple :
on produit immédiatement un travail propre pour les autres développeurs, et le manuel utilisateur en prime
dans Visual Studio .NET, avec la complétion automatique et l'intellisense, il est possible de savoir tout de suite le format de telle ou telle méthode, avec les paramètres attendus, les commentaires sur la classe, etc...
Lors de la génération, Visual Studio .NET génère des warnings si les informations de documentation d'une classe ne sont pas bien renseignées, ce qui permet au développeur de refaire une passe sur son travail pour tout vérifier et, éventuellement, déceler et prévenir très tôt des erreurs de logique éventuelles (comme de mal interpréter un paramètre d'une méthode parce qu'il n'est pas bien documenté)
L'outil que je propose ici s'appelle NDoc, un freeware dont la fonction est de regrouper les fichiers XML produit par Visual Studio .NET pour les compiler ensemble en un fichier d'aide au format standard CHM.
Ajoutons tout de même qu'en fonction de la taille du projet, cette génération de documentation pourra être réservée à une configuration bien précise (release uniquement par exemple), voire même un poste bien précis (comme sur le serveur de build uniquement) : encore une fois, c'est au chef de projet de décider de ce qu'il veut faire de l'outil.
Il suffit de suivre les étapes suivantes pour mettre en oeuvre NDoc:
Dans Visual Studio .NET, pour chaque assembly dont on veut générer la documentation, ouvrir la fenêtre de propriétés du projet puis, dans les "Propriétés de Configuration", spécifier le nom du fichier XML de documentation à produire. A chaque génération de l'assembly, Visual Studio .NET se base sur les commentaires au format XML qui accompagnent chaque classe afin de produire le fichier de documentation correspondant (en analysant les balises <summary>, <param>, <returns>, etc...). A ce niveau il est de bon ton de corriger tous les warnings générés par Visual Studio .NET à propos des commentaires XML manquants, etc... car c'est justement le but recherché :
|
Installer NDoc (il faut au préalable avoir installé l'utilitaire HtmlHelp de Microsoft). Ici encore, cet outil se présente sous deux formes : une interface graphique appelée "NDocGui" et un utilitaire en mode console appelé NDocConsole. Nous utiliserons NDocGui pour l'instant, avant de nous pencher plus tard sur NDocConsole
Créer un nouveau projet "NetEnvDev.ndoc", choisir le type de fichier à générer (MSDN par défaut) et bien spécifier le nom du fichier, le titre, le répertoire de sortie dans la propriété "OutputDirectory" (personnellement, j'utilise un sous répertoire "Chm" dans le répertoire de la solution) :
|
Ajouter les assembly à documenter dans le projet (uniquement celles qui sont nécessaires : quand nous aborderons les tests unitaires, nous verrons qu'il me parait préférable de les séparer physiquement des assembly à documenter)
Tout comme pour FxCop, intégrer ce fichier NetEnvDev.NDoc au contrôle de code source à l'aide de VSS Explorer (au même endroit que la solution)
Lancer la "compilation" de la documentation. NDoc produit alors un fichier NetEnvDev.chm du plus bel effet "à la MSDN" (il est nécessaire de recharger la solution dans VS .NET afin de profiter de la mise à jour de cette doc) :
|
Avoir un code source bien écrit et documenté n'est qu'une étape parmi d'autres: il faut s'assurer en même temps qu'il répond correctement aux tests unitaires, et ce à n'importe quelle étape de son développement.
La méthodologie appelée Test-Driven Development consiste, pour chaque classe, à écrire les tests unitaires correspondants avant d'écrire le code. On peut adhérer pleinement à cette technique (tout droit issue des principes de l'Extreme Programming) ou la considérer comme irréaliste, mais force est de reconnaître qu'elle a le mérite de mettre l'accent sur les tests unitaires (un peu à l'image du chapitre précédent sur la documentation de code).
Une des conditions de qualité des tests unitaires est d'être reproductibles, et donc théoriquement re-déroulés à chaque modification du code associé. Le faire manuellement est illusoire et peu fiable, et il s'agit donc d'automatiser le déroulement des tests unitaires en utilisant des outils adaptés. Je vous présente donc NUnit, un freeware (basé sur JUnit) permettant de créer des tests unitaires associés à chaque classe développée pour ensuite les exécuter autant de fois qu'on le désire.
Bien évidemment, l'écriture de tests unitaires a un coût, et il s'agit de les rendre les plus simples et les plus exhaustifs possibles. Ils ne sont pas non plus la panacée, ni garants de la totale fiabilité du code. Mais ils sont indispensables pour tout projet sérieux. A ce sujet, on se reportera sur des articles publiés par ailleurs, comme le très intéressant Tests unitaires en pratique. Mon propos est simplement de montrer comment les mettre en oeuvre.
Le principe de fonctionnement de NUnit est relativement simple :
à chaque classe on associe une nouvelle classe de tests unitaires. Par commodité je préfère créer une assembly séparée (mais c'est un choix strictement personnel, qui devrait d'ailleurs être réfléchi pour chaque projet) d'autant plus que je ne désire pas générer de documentation pour ces classes de tests. La classe de tests doit être marquée de l'attribut NUnit : [TestFixture]
chacune des classes de tests comporte des méthodes de tests marquées de l'attribut NUnit : [Test]. Typiquement, chaque méthode effectue un ou plusieurs tests unitaires d'une méthode de la classe associée (en l'appelant par exemple avec différentes valeurs d'entrée, conditions aux limites, valeurs à 0, etc...). Une méthode de test utilise la classe NUnit Assert et ses méthodes IsTrue() et IsFalse() pour tester unitairement le résultat de l'appel d'une méthode de la classe associée et donc indiquer si le test est OK ou non
NUnit est ensuite utilisé pour effectuer automatiquement l'ensemble des tests unitaires contenus dans une ou plusieurs assembly et stocker le résultat, qui peut être affiché dans l'interface graphique de NUnit, en rouge ou en vert, selon que les tests unitaires ont été réalisés avec succès ou non
Une fois compris les principes de NUnit, sa mise en oeuvre est directe sans toutefois être immédiate :
installer NUnit. Ici encore il se présente sous la forme d'une interface graphique appelée "NUnit-Gui" et d'un utilitaire en mode console appelé "NUnit-Console". Et ici encore nous n'utiliserons que NUnit-gui
dans la solution NetEnvDev, créer un nouveau projet (une librairie de classes C# appelée par exemple "MainLibTest", que je positionne de préférence dans un sous répertoire du projet MainLib associé) et y ajouter une classe "MainClassTest" marquée de l'attribut [TestFixture] et destinée à tester unitairement la classe MainClass. Ne pas oublier d'y ajouter la référence à l'assembly externe nunit.framework, et y déclarer une instance de la classe MainClass qui nous servira à tester ses méthodes. Au passage, l'ajout d'un projet dans une solution sous contrôle de code source est un exercice pratique intéressant :
|
ajouter une méthode "AddTest" marquée de l'attribut [Test] à la classe MainClassTest qui permet de tester la méthode "Add" de l'instance de la classe MainClass en fournissant différentes valeurs à la méthode et en spécifiant le résultat attendu (remarquez l'emploi de l'attribut NUnit [SetUp] de la méthode SetUp() de la classe MainLibTest qui permet de définir une méthode appelée avant chaque test unitaire, comme ici pour créer l'instance de la classe à tester):
|
générer la solution
dans NUnit-gui, créer un nouveau projet appelé "NetEnvDev.nunit" dans le répertoire de la solution et ajouter l'assembly "MainLibTest" au projet
intégrer ce fichier NetEnvDev.nunit au contrôle de code source à l'aide de VSS Explorer
Il suffit ensuite de cliquer sur "Run" pour exécuter les tests :
|
à titre d'exemple, il suffit de modifier la méthode Add() pour qu'elle génère un résultat faux, puis de re-dérouler les mêmes tests unitaires, afin de voir à quel point NUnit est simple d'emploi tout en étant très pratique:
|
Ceci n'est bien sûr qu'un résumé succinct des possibilités de NUnit, que je vous encourage vivement à approfondir.
Pour résumer, un développeur qui aborde un cycle d'écriture de code devrait respecter (au moins) les principes suivants:
extraire le code du référentiel
apporter les modifications dans le code
vérifier systématiquement le code écrit avec FxCop
documenter le code produit et générer la documentation avec NDoc
écrire des tests unitaires pour le code ainsi développé
dérouler les tests unitaires avec NUnit
uniquement si OK : archiver le code dans le référentiel
Une règle d'or à observer en effet est qu'il ne faut jamais archiver dans le référentiel du code qui n'a pas été vérifié, ni documenté, et encore moins testé.
Il faut bien se rendre compte cependant que l'utilisation systématique de ces outils finit par être répétitive et, du coup, rébarbative. Il s'agit d'aider le développeur dans son travail de tous les jours, et non pas de le retarder avec des outils fastidieux qu'il va s'empresser de négliger ! L'idéal donc serait de pouvoir automatiser cet enchaînement de tâches et de laisser au développeur le soin de se concentrer sur l'écriture de son code, et les corrections éventuelles à apporter.
Or Visual Studio .NET par exemple ne permet *que* de compiler. De plus il est prévu de ne pas l'installer sur le serveur de build (le SDK uniquement doit suffire à compiler).
Il s'agit donc de pouvoir dénicher un outil de build automatique qui permet de compiler, vérifier le code source, documenter et effectuer les tests unitaires, le tout par une simple ligne de commande lancée manuellement sur chaque poste de développement, à chaque fois qu'une modification est effectuée sur le code source extrait localement, et avant qu'il ne soit réintégré dans le référentiel. Bien entendu, le développeur continue toujours à utiliser toujours Visual Studio .NET pour compiler plusieurs fois, comme à son habitude, et procéder régulièrement à une génération plus complète avec l'outil de build, localement, afin de déceler des erreurs ou simplement de vérifier et de tester le code produit.
Un tel outil est donc déjà précieux sur chaque poste de développement, mais répétons qu'il n'est pas nécessaire car chaque développeur dispose des interfaces graphiques de chaque outil sur son poste, et peut donc les utiliser pour effectuer le même travail que l'outil de build, mais manuellement, s'il le désire.
La mise en oeuvre d'un outil de build se révèle par contre totalement indispensable dans le cas du serveur de build, qui fait office de référence pour tout ce qui concerne les exécutables produits et qui doit donc avoir été passé au crible de tous nos outils (qui vont alors s'exécuter en mode console).
Bonne nouvelle : un tel outil de build existe, il est gratuit, solide et simple, et il s'appelle NAnt.
Il suffit d'essayer une fois avec succès un outil de build pour ne plus jamais pouvoir s'en passer. Avec le freeware NAnt (issu de la version Ant du monde Java) les services rendus sont considérables, alors que les principes de base sont excessivement simples (même si parfois on est confrontés à quelques petits casse-têtes de configuration, en fonction de la taille du projet) :
NAnt est un utilitaire en mode console, qui se base sur un fichier de configuration au format XML qui doit se trouver dans le même répertoire et avoir l'extension ".build" : dans notre cas nous allons utiliser un fichier appelé "NetEnvDev.build"
NAnt est chargé d'effectuer un nombre quelconque de "tâches" regroupées dans le fichier de configuration dans des balises <target></target>. Les taches peuvent être nommées, afin par exemple de pouvoir dépendre d'une ou plusieurs autres tâches, qui seront alors exécutées avant, comme dans le cas suivant où le groupe de tâches appelées "build" doivent être exécutées avant le groupe de tâches appelées "tests" :
<target
name="build" />
<target
name="tests" depends="build"/>
au sein d'une <target>, on spécifie la tâche que NAnt doit exécuter par des balises XML dont les attributs contrôlent l'exécution de la tâche; NAnt est capable d'exécuter un éventail très large de tâches différentes
NAnt permet aussi de définir des "propriétés" qui permettent de paramétrer l'exécution du build
En somme, NAnt n'est ni plus ni moins qu'un "make" très évolué, destiné à l'environnement .NET. Pour l'exploiter il s'agit donc de créer un projet de configuration NAnt qui va effectuer les tâches suivantes dans l'ordre :
sur le serveur de build uniquement : extraire la dernière version des sources du référentiel VSS dans le répertoire local
compiler l'intégralité du code source local
lancer par FxCopCmd la vérification de l'intégralité du code source local
lancer par NDocConsole la génération de l'intégralité de la documentation liée au code source local
lancer par NUnit-Console l'exécution des tests unitaires sur le code source local
Il faut noter l'existence de l'utilitaire NAntContrib, qui regroupe un ensemble de tâches non prises en charge par NAnt (comme l'extraction VSS par exemple). Il suffit d'installer NAntContrib dans le répertoire de NAnt pour avoir accès à ces tâches supplémentaires.
Les tâches suivantes sont très courantes :
<delete> : permet de supprimer des fichiers :
<target name="clean" />
<delete file="myfile.txt" />
</target>
<vssget> : permet d'extraire la dernière version d'un référentiel VSS. Pour notre projet NetEnvDev par exemple, le serveur de build extrait les fichiers depuis VSS avec un compte utilisateur particulier appelé "build", stocke ces fichiers en local dans un répertoire "G:\DotNet\Projets\DotNetGuru\NetEnvDev", et accède à la base de données VSS localement puisqu'il fait aussi office de serveur VSS (remplacer le mot de passe par le vôtre):
<target name="vss" depends="clean" />
<vssget
user="build"
password="******"
localpath="G:\DotNet\Projets\DotNetGuru\NetEnvDev"
recursive="true"
replace="true"
writable="true"
dbpath="G:\DotNet\VSS\srcsafe.ini"
path="$/NetEnvDev/NetEnvDev"
/>
</target>
<solution> : permet de compiler l'intégralité d'une solution Visual Studio .NET (avec respect des références, en fait exactement comme si on lançait la compilation depuis Visual Studio .NET) :
<target name="build" depends="vss" />
<solution configuration="release" solutionfile="NetEnvDev.sln" />
</target>
<exec> : permet d'exécuter une commande. Cette tâche va nous permettre par exemple de lancer FxCop ou NDoc (remarquez au passage que le résultat de l'analyse par FxCop est produite dans un fichier XML):
<target name="fxcop" depends="build" description="Analyse du code source par FxCop">
<exec basedir="C:\program files\Microsoft FxCop 1.23"
workingdir="${build.dir}"
program="FxCopCmd.exe"
commandline="/project:NetEnvDev.FxCop /out:${FxCopResultFile}"
failonerror="false"
/>
</target>
<nunit2>: permet de lancer directement une série de tests unitaires NUnit :
<target name="tests" depends="build" />
<nunit2>
<formatter type="Plain" />
<test assemblyname="MainLibTest.dll"/>
</nunit2>
</target>
NAnt génère des fichiers résultats au format XML, et ne passe à une nouvelle étape que si l'étape précédente n'a pas généré d'erreurs.
Un cycle de build avec NAnt consiste à :
lancer le build, à la main pour l'instant (avant de songer à une solution plus automatisée, voir chapitre suivant !)
récupérer le résultat d'une façon ou d'une autre
analyser chaque build défaillant pour en trouver la raison avant de corriger le code fautif
Afin de mettre en oeuvre NAnt dans notre environnement de développement, il suffit de :
installer NAnt sur le serveur de build, et également en option sur les postes de développement afin que chaque développeur puisse avoir accès à une version "réduite" du fichier de build (qui n'extrait pas les sources VSS par exemple, car le build doit opérer simplement sur les sources locales, avant archivage)
en fonction de la version du Framework .NET que vous utilisez, positionner la valeur de l'attribut "default" de la balise <platform> dans la section <framework> du fichier nant.exe.config dans le répertoire /bin de NAnt :
<platform name="win32" default="net-1.1">
Reproduire la même arborescence de répertoires que sur les postes de développement (l'ensemble des fichiers du projet, dont la solution et les fichiers .FxCop, etc... seront récupérés depuis VSS par la tâche <vssget> de NAnt)
L'intérêt des différents projets FxCop, NDoc, que nous avons créés précédemment est qu'ils peuvent être directement utilisés par la version console des outils, au travers de tâches <exec> que NAnt peut exécuter en analysant le fichier NetEnvDev.build suivant situé dans le répertoire de la solution - le projet NUnit par contre n'est pas nécessaire car il existe une tâche NAnt dédiée
Les résultats de FxCop et NUnit doivent produire des fichiers XML, de préférence dans le même emplacement (par exemple dans un répertoire "BuildResults" dans le répertoire de la solution)
Dans le projet NetEnvDev.build que voici, remarquez que j'ai configuré NUnit et FxCop pour produire des fichiers de résultats dans un répertoire unique appelé "BuildResults" situé au niveau de la solution, que j'utilise une tâche "go" par défaut qui sert uniquement à lancer toutes les autres dans l'ordre, et que je fais en sorte que la target "FxCop" n'arrête pas le build même s'il y a une erreur (qui peut être due notamment au fait que FxCop n'importe pas correctement ses propres résultats d'analyse précédents ;-) :
<?xml
version="1.0" ?>
<project name="NetEnvDev" default="go" basedir=".">
<property name = "build.dir" value =
"." />
<property name = "FxCopResultFile"
value = "${build.dir}\BuildResults\FxCopResult.xml" />
<target name="go" depends = "vss,build,fxcop,ndoc,test">
</target>
<target name="vss"
description="Extraction des fichiers depuis VSS">
<vssget
user="build"
password="******"
localpath="G:\DotNet\Projets\DotNetGuru\NetEnvDev"
recursive="true"
replace="true"
writable="true"
dbpath="G:\DotNet\VSS\srcsafe.ini"
path="$/NetEnvDev/NetEnvDev"
/>
</target>
<target name="build" depends="vss"
description="Build general de la solution">
<solution
solutionfile="NetEnvDev.sln" configuration="release"/>
</target>
<target name="fxcop" depends="build"
description="Analyse du code source par FxCop">
<exec
basedir="C:/program files/Microsoft FxCop 1.23"
workingdir="${build.dir}"
program="FxCopCmd.exe"
commandline="/project:NetEnvDev.fxCop /update /out:${FxCopResultFile}"
failonerror="false"
/>
</target>
<target name="ndoc" depends="fxcop"
description="Generation de la documentation par NDoc">
<exec
basedir="C:\program
files\NDoc 2.1\bin\.net-1.1"
workingdir="${build.dir}"
program="NDocConsole.exe"
commandline="-project=NetEnvDev.NDoc"
failonerror="true"
/>
</target>
<target name="test" depends="build"
description="Tests unitaires sur la solution par NUnit">
<nunit2>
<formatter type="Xml" usefile="false" extension=".xml" outputdir="${build.dir}\BuildResults"/>
<test assemblyname="${build.dir}\MainLib\MainLibTest\bin\release\MainLibTest.dll"
/>
</nunit2>
</target>
</project>
ce fichier "NetEnvDev.build" doit être créé sur le serveur de build dans le répertoire de la solution; il ne reste plus ensuite qu'à lancer NAnt dans ce même répertoire, de façon à ce qu'il puisse analyser le fichier NetEnvDev.build et lancer les tâches désirées (au final, le build devrait être réussi, sinon il faut analyser la sortie de NAnt, voire les fichiers XML FxCop et NUnit pour en trouver la raison).
Après tous ces efforts, nous disposons maintenant sur le serveur de build d'un build complet et reproductible! Il manque encore, cependant, l'automatisation nécessaire pour compléter notre environnement et le rendre véritablement puissant et exploitable, à savoir :
la faculté de déclencher un build automatiquement sur le serveur de build, de façon régulière et continue, afin de s'assurer qu'on dispose toujours d'une version exécutable propre
la faculté de pouvoir être avertis du résultat d'un build déclenché automatiquement, réussi ou non, par exemple par mail au cas où les équipes de développement sont dispersées géographiquement et que les sources sont partagées via Internet
la faculté de pouvoir archiver les versions des builds, et de pouvoir consulter les résultats de ces builds sur une période donnée, et pourquoi pas par Internet ?
En bref, il nous faut un outil capable de travailler avec NAnt pour contrôler ce qu'on appelle un processus de build.
Il existe un logiciel qui remplit toutes ces fonctions (et même plus encore), gratuitement de surcroît : j'ai nommé CruiseControl .NET. Il est développé par la société ThoughtWorks, dans laquelle officie un personnage bien connu appelé Martin Fowler, qui est l'un des chefs de file d'une méthodologie en vogue appelée Intégration Continue, qui part du principe bien-fondé selon lequel :
tout archivage de fichier dans un référentiel sous contrôle de code source devrait provoquer un build automatique
tout build défaillant devrait être immédiatement corrigé
On est ainsi amenés à effectuer des builds généraux tout au long d'une journée de travail, selon la cadence des archivages de fichiers effectués. D'où la nécessité impérative pour les développeurs de s'assurer au préalable que le code qu'ils archivent est propre. La référence est le build effectué par NAnt sur le serveur de build, en se basant sur les dernières archives effectuées.
Ces builds généraux automatiques et journaliers (on peut en compter plus d'une douzaine par jour, pourquoi pas) garantissent qu'on dispose toujours d'une version exécutable, propre, documentée, et ayant passé avec succès les tests unitaires. Il reste ensuite à gérer les archivages de version des exécutables pour disposer au final d'un environnement de développement solide et automatisé au maximum.
CruiseControl .NET se présente sous la forme soit d'un utilitaire en mode console, soit d'un service. Il s'exécute continuellement, et repose lui aussi sur un fonctionnement très simple :
il se configure à l'aide d'un fichier XML appelé ccnet.config (qu'il recharge automatiquement à chaque modification, sans avoir besoin de l'arrêter)
il surveille (périodiquement) le référentiel de contrôle de code source (par exemple VSS) et détecte s'il y a eu des changements en analysant l'historique des archivages effectués et des modifications apportées; si le référentiel de code source a été modifié, alors il déclenche automatiquement un build par NAnt
il peut stocker les résultats du build dans des fichiers XML, et peut également y fusionner n'importe quel autre fichier XML, comme par exemple les résultats de NUnit et de FxCop
il peut prévenir par mail des résultats d'un build
il propose une interface Web pour visualiser les résultats des différents builds effectués
Remarque : la raison pour laquelle il faut installer la version anglaise de VSS se trouve ici : pour analyser si le référentiel VSS a été modifié, CruiseControl .NET analyse la sortie au format texte de la commande "history" de VSS... or cette sortie doit être formatée en anglais sinon CruiseControl .NET ne détecte aucune modification dans le référentiel, comme c'est le cas si on utilise la version française de VSS !
Le fichier "ccnet.config" doit être créé sur le serveur de build au même niveau que le fichier de build de NAnt. Je vais me contenter de vous livrer ce fichier en m'attardant sur certaines sections essentielles du fichier (je vous renvoie à la documentation de CruiseControl .NET pour plus de précisions) :
<schedule> : donne l'intervalle de temps minimum entre deux intégrations. Par exemple, la section suivante indique à CruiseControl .NET de vérifier le référentiel de code source toutes les 5 minutes :
<schedule type="schedule" sleepSeconds="300"</schedule>
<sourcecontrol> : indique le type de contrôle de code source utilisé et l'emplacement de la base de données correspondante dont il doit surveiller les modifications. Par exemple, dans notre cas, on spécifie VSS :
<sourcecontrol type="vss">
<executable>"C:\Program Files\Microsoft Visual Studio\VSS\win32\SS.EXE"</executable>
<project>$/NetEnvDev</project>
<username>build</username>
<password>******</password>
<ssdir>G:\DotNet\VSS</ssdir>
</sourcecontrol>
<build> : indique le type de build à effectuer en cas de modifications du référentiel de code source. Ici on utilise NAnt sur le fichier "NetEnvDev.build" déjà éprouvé dans le répertoire local où on extrait les sources depuis VSS (c'est NAnt qui est chargé d'extraire les sources du référentiel, et pas CruiseControl .NET qui ne fait que lancer le build):
<build type="nant">
<executable>c:\program files\NAnt 0.84\bin\Nant.exe</executable>
<baseDirectory>G:\DotNet\Projets\DotNetGuru\NetEnvDev</baseDirectory>
<buildFile>NetEnvDev.build</buildFile>
<buildTimeoutSeconds>300</buildTimeoutSeconds>
</build>
<xmllogger> : indique que nous voulons que CruiseControl .NET génère ses résultats au format XML dans un répertoire particulier donné par <logDir>. De plus, avec la section <mergeFiles> il est possible de fusionner ces logs avec d'autres fichiers XML, comme ici avec les résultats de FxCop et de tous les fichiers NUnit (qui ont tous la forme "assembly-results.xml"):
<publishers>
<xmllogger>
<logDir>C:\program files\CruiseControl .NET\web\log</logDir>
<mergeFiles>
<file>G:\DotNet\Projets\DotNetGuru\NetEnvDev\BuildResults\FxCopResult.xml</file>
<file>G:\DotNet\Projets\DotNetGuru\NetEnvDev\BuildResults\*-results.xml</file>
</mergeFiles>
</xmllogger>
</publishers>
Le fichier ccnet.config pour notre application NetEnvDev est alors le suivant:
<cruisecontrol>
<project name="NetEnvDev">
<schedule type="schedule" sleepSeconds="300"></schedule>
<modificationDelaySeconds>10</modificationDelaySeconds>
<sourcecontrol type="vss">
<executable>"C:\program files\Microsoft Visual Studio\VSS\win32\SS.EXE"</executable>
<project>$\NetEnvDev</project>
<username>build</username>
<password>******</password>
<ssdir>G:\DotNet\VSS</ssdir>
</sourcecontrol>
<build type="nant">
<executable>"C:\program files\Nant 0.84\bin\Nant.exe"</executable>
<baseDirectory>G:\DotNet\Projets\DotNetGuru\NetEnvDev</baseDirectory>
<buildFile>NetEnvDev.build</buildFile>
<buildTimeoutSeconds>300</buildTimeoutSeconds>
</build>
<publishers>
<xmllogger>
<logDir>C:\program files\CruiseControl .NET\web\log</logDir>
<mergeFiles>
<file>G:\DotNet\Projets\DotNetGuru\NetEnvDev\BuildResults\FxCopResult.xml</file>
<file>G:\DotNet\Projets\DotNetGuru\NetEnvDev\BuildResults\*-results.xml</file>
</mergeFiles>
</xmllogger>
</publishers>
</project>
</cruisecontrol>
Il suffit ensuite de lancer CruiseControl .NET en ligne de commande au niveau du répertoire où se trouve ce fichier. Si tout est bien configuré il n'y a qu'à modifier un fichier source dans VSS depuis un poste de développement, puis archiver le fichier dans VSS pour déclencher un build par CCNET sur le serveur de build :
|
Pouvoir effectuer des builds automatiques sur détection de changement dans le code source, c'est bien, pouvoir visualiser les résultats de tous les builds à travers une interface Web, c'est mieux ! Dans notre exemple nous allons faire en sorte que le serveur de build puisse exposer les résultats de ses builds automatiques via un site Web consultable depuis n'importe quel poste sur le réseau, et pour cela il faut :
installer IIS et ASP.NET sur le serveur de build
dans IIS, créer un nouveau répertoire virtuel (avec l'alias "CCNETWebApp" par exemple) qui correspond au sous répertoire "web" du répertoire d'installation de CruiseControl .NET (et dans lequel se trouvent les fichiers de l'interface Web : default.aspx, FxCop.aspx, etc...)
dans le fichier ccnet.config, ajouter la section <webURL> suivante juste après <project>, pour permettre aux utilisateurs d'accéder au site Web (remplacer "CCNETWebApp" par l'alias que vous avez donné au répertoire virtuel) :
<webURL>http://localhost/CCNETWebApp/</webURL>
lancer internet explorer puis tapez l'URL "http://nom_server_build/CCNETWebapp/default.aspx" pour accéder à l'interface Web de CruiseControl .NET, où "nom_server_build" est le nom de votre serveur de build (remarquez la liste des builds à gauche, les liens vers les fichiers NUnit et FxCop, et les détails sur chaque build réussi ou échoué) :
|
Nous sommes arrivés au terme de notre petit tour d'horizon des outils indispensables à la mise en place d'un environnement de développement homogène, solide et fiable.
Mais nous sommes bien sûr très loin d'avoir fait tour de tous les outils existants ! Hormis le fait que certains outils que j'ai présenté peuvent être entièrement remplacés par d'autres, il y a des parties du cycle de développement d'un logiciel que je n'ai pas évoqué : par exemple les tests fonctionnels automatiques sur les interfaces ASP.NET, qui peuvent être pris en charge par exemple par NUnitASP, etc...
Pour être complet, il manque également un outil de gestion des versions des builds, inspiré par exemple d'un logiciel comme BuildIt.
Cela fera peut-être l'occasion d'un prochain article...
Auteur : Laurent Desmons
Copyright © Mai 2004
|
Ses clients sont de grands comptes de
la métallurgie et sidérurgie (Péchiney, Arcelor) et il est amené à
développer essentiellement des applications client/serveur de plus ou
moins grande envergure, en environnement Windows. Des développements
réalisés en C++/C# pour la partie processus et en VB/C++/MFC/C#
pour la partie IHM. |
Ressources
Site officiel de Visual SourceSafe : http://msdn.microsoft.com/vstudio/previous/ssafe/
FAQ Visual SourceSafe : http://www.michaelis.net/SourceSafe/Faq.htm
Un document essentiel : Team development with Visual Studio .NET and Visual SourceSafe
CVSNT Wiki : http://www.cvsnt.org/wiki/
Igloo : http://www.jalindi.com/igloo/
PushOK : http://www.pushok.com/
Site officiel de Perforce : http://www.perforce.com/
Un article sur les Enterprise Templates : http://www.15seconds.com/issue/030122.htm
Site officiel de FxCop : http://www.gotdotnet.com/team/fxcop/
Site officiel de NDoc: http://sourceforge.net/projects/ndoc/
Test-driven development : http://c2.com/cgi/wiki?TestDrivenDevelopment
Une présentation de l'Extreme Programming (XP) : http://www.xprogramming.com/xpmag/whatisxp.htm
Site officiel de NUnit : http://www.nunit.org/
Mise en oeuvre de NUnit : http://www.xprogramming.com/xpmag/acsUsingNUnit.htm
Un article sur les tests unitaires: http://www.dotnetguru.org/articles/dossiers/testunitaires/UnitTest.htm
Site officiel de NAnt : http://nant.sourceforge.net/
Un article sur NAnt sur TheServerSide .NET : Managing .NET development with NAnt
Site officiel de NAntContrib : http://nantcontrib.sourceforge.net/
Site officiel de CruiseControl .NET : http://ccnet.thoughtworks.com/
L'article de référence de Martin Fowler : Continuous Integration
Le site officiel de BuildIt .NET : http://www.microsoft.com/downloads/details.aspx?FamilyId=B32497B0-77F7-4831-9C55-58BF3962163E&displaylang=en