Articles de mathieu :
- Définir le test, en particulier son nom
- Ecrire les assertions (ce que je vérifie)
- Définir mes besoins (contexte, instanciation des composants testés)
- Appel de la routine à tester
- Je déclare les différentes variables
- j’implémente au minimum l’objet du test (création du type, de la routine)
- Undo : annulation des modifications en attentes
- Get : Récupération de la dernière version (facultatif)
- Unshelve : Récupération du cadre de développement de la fonctionnalité à savoir mon test
- Définir quelle était la cause d’une modification du contrôleur de code source
- Déterminer l’impact de l’implémentation d’une fonctionnalité ou d’un correctif
- Démarrage des builds basé sur les évènements du contrôleur de source
- la configuration est facile et permet d’empiler des compilations et de définir le nombre de builds à conserver afin de limiter l’espace disque utilisé par exemple
- …
- Que l’installation s’achève sans autre avertissemnet
- Que les fichiers *.dim.xml ont été regénérés
Je mocke, tu mockes, il mocke… nous loupons un refactoring
Avant de mocker un composant, assurez vous que les difficultés que vous rencontrez à le tester ne proviennent pas du composant lui même.
PS : Je sais c’est court mais je crois que c’est pertinent et que le message passera mieux. D’autant que je m’en fait un pense bête à lire 10x ou 15x par jour tellement c’est évident et qu’on arrive à passer à côté.
Test d’intégrataire ou test d’unigration
Ces dernières semaines durant mon développement ou autour d’un autre binôme à Pyxis, j’ai été confronté à cette discussion qui trainait et qui au final ralentissait le développement :
Membre de l’équipe 1 : A ton avis, c’est un test d’intégration ou un test unitaire?
Membre de l’équipe 2 : ouf, y’a un fichier XML à utiliser, je vois pas comment s’en passer sans perdre le sens du test, çà doit forcement être un test d’intégration.
Membre de l’équipe 1 : Oui mais, il y a pleins de traitement différents que j’aimerais tester unitairement alors faire un test d’intégration avec le fichier…
Membre de l’équipe 2 : bon bah on va faire deux tests alors. Mais on fait quoi en premier?
Membre de l’équipe 1 : Bah c’est évident, on commence par l’unitaire.
Membre de l’équipe 2 : Et on fait comment pour le fichier?
Constat
A partir de ce point, on tourne en rond. On cherche à tester unitairement pour se rendre compte que l’on n’arrive pas à isoler ce maudit fichier.
Pattern
De ce que j’ai pu observé dans les différentes situations auxquelles j’ai été confrontées, le problème était le même. La question ne venait pas de la façon d’aborder les tests du composant mais bien le composant en lui même. Celui-ci dépendait d’une ressource (fichier XML, base de données, Service distant) lui fournissant des services de données. Ce même composant proposait un certain nombres de routines de traitement de ces mêmes données.
Logique + données, pourquoi ne pas séparer?
Au final, la résolution passait par une séparation de ces deux aspects du comportement. On extrait la partie accès aux données et la partie traitement. Rien de novateur, là dedans, ce qui l’est davantage pour moi (et je pense que vous en conviendrez), c’est que les tests ont facilité de détecter ce pattern et par expérience désormais si j’entend un développeur se posait la question : Test d’intégration ou test unitaire? je lui suggérerais en tout premier lieu de revoir le code ciblé par le test( ou l’idée qu’il s’en faisait en TDD).
Rhino Mock / MSTest : Isolation
Dans ce post, j’aimerais vous présenter diverses méthodes permettant d’effectuer des tests de comportements sur vos composants. On ne teste pas le contexte, les données retournées mais bien le comportement de la routine avec l’appelant et les dépendances.
Contexte
On souhaite tester une méthode d’un dépôt(notion de repository en DDD). La routine en question est un service permettant d’envoyer des entités (Demande) à un service Web pour centraliser ces demandes. Les différentes actions effectuées sont les suivantes :
L’appelant appelle la méthode EnvoyerTout prenant en paramètre une liste de Demande.
Le dépôt reçoit la liste et vérifie les informations de synchronisation dans une base de données locale.
Le dépôt envoie la liste des demandes et les informations de synchronisation au service.
Le service répond et fournit des informations permettant d’enregistrer en local que les demandes ont déjà été envoyé au serveur.
Afin de limiter le trafic réseau, on souhaite vérifier que les demandes réellement envoyées au serveur n’avait jamais été envoyées. Cette information est disponible dans la base de données de locale.
On souhaite isoler le comportement de cette routine car nous détectons rapidement les différentes dépendances :
Un service Web qui doit répondre aux différentes requêtes du proxy.
Une base de données contenant des informations de synchronisation.
Une utilisation d’une dépôt permettant de récupérer les informations de l’utilisateur.
Explication du test
Voici donc le test en question :
1: [TestMethod]
2: public void EnvoyerToutNAjouteQueLesDemandesJamaisSynchronisees()
3: {
4: utilisateurDepot = mockRepository.Stub<IUtilisateurDepot>();
5: demandeDeControleService = mockRepository.StrictMock<DemandeDeControleService>();
6: demandeDeControleServeurDepot = mockRepository.Stub<DemandeDeControleServeurDepot>(demandeDeControleService, utilisateurDepot);
7: Expect.Call(demandeDeControleServeurDepot.GetReplicaId()).Return(Guid.NewGuid());
8: demandeDeControleServeurDepot.MettreAJourTousLesWatermark(null, 0);
9: LastCall.IgnoreArguments();
10:
11: Expect.Call(demandeDeControleService.EnvoyerTout(null)).IgnoreArguments().Return(0).Repeat.Once();
12: mockRepository.ReplayAll();
13:
14: demandeDeControleServeurDepot.EnvoyerTout(CreerUneListeDeDemandes(5, 2));
15:
16: ServiceDomain.DemandeDeControle[] demandeDeControleEnvoyees =
17: (ServiceDomain.DemandeDeControle[])demandeDeControleService.GetArgumentsForCallsMadeOn(x => x.EnvoyerTout(null)).First().First();
18: Assert.AreEqual(2, demandeDeControleEnvoyees.Length);
19: }
Pour décrire ce test commençons par le commencement d’un test à savoir son assertion :
Ligne 18 : Assert.AreEqual(2, demandeDeControleEnvoyees.Length); On vérifie ici que le nombre de demandes envoyés au serveur est bien égale à 2.
On pourrait donc traduire cette assertion de la sorte : Si l’application fournit une liste de 5 demandes au dépôt mais que seuls 2 demandes de cette liste doivent être envoyées au serveur, vérifie que le dépôt n’a envoyé que ces 2 éléments au service.
Pourquoi?
Ligne 14 : demandeDeControleServeurDepot.EnvoyerTout(CreerUneListeDeDemandes(5, 2)); C’est la routine que nous souhaitons tester. Dans le test, le paramètre (la liste de demandes suceptibles d’être envoyées) est fourni par une méthode du test CreerUneListeDeDemandes(5, 2) prenant en paramètre le nombre total de demande à créer et le nombre de demandes à synchroniser (et qui doivent donc réellement être envoyées au serveur).
Et la rapport avec l’assertion?
Ligne 16 et 17 : On récupère le premier paramètre du premier appel (il n’y en a qu’un) de l’exécution de la méthode EnvoyerTout d’un mock (demandeDeControleService) .C’est le cœur même du test et l’objet de l’assertion. Ces paramètres proviennet directement du mock, le service n’est jamais instancié.
Et comment c’est possible?
De la ligne 4 à la ligne 9 : on prépare l’exécution du test en isolant, à l’aide de diverses techniques et fonctionnalités de RhinoMock, les dépendances détectées.
Ligne 4 : on crée un stub du dépot utilisateur, aucun comportement ou données ne nous sont nécessaires. Nous avons juste besoin d’une instance de ce dépot pour l’instanciation ligne 6. Le fait de le “stuber” permet de ne pas exécuter le code de ce dépôt.
Ligne 5 : On crée un mock strict. Toutes les fonctionnalités du service seront recrées et tous les appels vers le service seront redirigés vers ce mock.
Ligne 6 à 12 : Création du mock partiel du composant testé. Toutes les routines réclamant un contexte (ligne 7 et 8, la récupération des informations de synchronisation nécessite une base de données locale) sont redirigés vers le mock. On peut configurer le résultat de l’exécution de la méthode du mock (ligne6 : demandeDeControleServeurDepot.GetReplicaId()).Return(Guid.NewGuid()) ) ou l’on peut déclarer d’ignorer l’appel et les paramètres (ligne 7).
Sur ce mock partiel, on appelle la méthode testée (ligne 11).
Tous les comportement d’isolation ne seront disponibles qu’après la ligne 12. Celle-ci permet d’informer RhinoMock des différentes instructions d’isolation.
Conclusion
Pas de base de données à construire et de données à générer. Pas de service sur un serveur de test. Mais le comportement attendu est vérifié et le sera même en cas de changement de source de données, de modification du contrat de service…
Cool non?
Mise sur étagère : ou comment revoir un flux de développement TDD avec TFS
La mise sur étagère dans Team Foundation Version Control est l’une des fonctionnalités qui m’a le plus rapidement séduit.
Classiquement, on s’en sert pour enregistrer une modification du source non achevée ( not done) mais que l’on souhaite conserver sur un serveur sauvegardé pour le récupérer en cas d’incident sur notre propre poste. Martin avait écrit un excellent billet à ce sujet.
On peut également l’utiliser pour communiquer nos modifications sur notre espace de travail avec d’autres développeurs, pour fusionner le travail, lors d’un binômage etc…
Récemment, j’ai surtout pris l’habitude de mettre sur étagère dans mon flux de développement en TDD (ou plutôt en Test First Programming, certains connaissent mon attachement à cette différenciation).
Voici donc ce nouveau flux :
Définir le test
Obtenir le résultat du test (Red bar)
A ce stade, le projet ne compile pas en général, puisque rien n’est implémenté. L’objectif suivant est de faire compiler ce projet.
Ici, j’en arrive au point ou je compile et il ne me reste qu’à implémenter la fonctionnalité avec l’assurance de tester automatiquement celle-ci. C’est pour moi le moment idéal d’effectuer une mise sur étagère des modifications de mon espace de travail. Celui-ci me permet d’être plus à l’aise dans l’implémentation, en cas de problème, je peux revenir en arrière par l’ensemble d’actions :
Essayez ce flux, c’est l’adopter.
La fonctionnalité : Lier à un élément de travail
Etant actuellement impliqué dans un projet agile autour de Team Foundation Server (un peu plus à ce sujet très prochainement), j’ai décidé de faire partager mon expérience du produit lors de chacune des Daily Meetings. C’est donc l’occasion pour moi de revenir sur mes basiques et surtout de faire un point sur ce qu’on attend des fonctionnalités de Team Foundation Server et ce qu’elles apportent réellement à l’équipe.
Lier à un élément de travail
Description : Lors de l’archivage d’une modification du projet, le développeur a l’opportunité de lier cette modification à un ou plusieurs éléments de travail. Un lien est alors créé entre son jeu de modifications (changeset) et l’élément de travail.
1 : Lors de l’archivage, le 2° bouton dans le menu de gauche permet de lier la modification à un élément de travail
2 : une requête permet de sélectionner l’élément de travail (par défaut les éléments de travail qui me sont assignés : “My Work Items”)
3 : Le développeur peut agir directement sur l’élément de travail en sélectionnant l’action (associer l’élément de travail ou le résoudre)
Une fois l’archivage effectué, le lien est présent dans l’historique du contrôleur de code source à l’aide dans le détail de l’archivage et depuis la base de l’élément de travail à l’aide d’un lien vers le contrôleur de code souce.
Les apports
En deux clics de la part du développeur on obtient une traçabilité “technico fonctionnelle”. J’entends par là que chacune des actions des développeurs, des testeurs et des architectes sont réliées à la demande ou au besoin initial. N’oublions pas que l’ensemble des ses informations vont persistées dans la base (SQL Server) pour des années et permettront de naviguer dans l’historique du projet et ainsi de :
Mise en place et Risques
Pas de mise en place particulière, il suffit d’avoir des éléments de travail à lier à vos modifications. Il s’agit donc de préparer les développements et les différentes tâches de l’équipe. Au niveau des risques, il est important de lier chaque modification du code source à un et un seul élément de travail. Rappelons que lors d’un archivage, tous les fichiers modifiés dans l’espace de travail sont sélectionnées mais qu’il est possible de construire son jeu de modifications en sélectionnant les seuls fichiers qui ont permis d’implémenter la fonctionnalité ou de résoudre un bug.
Contourner l’intégration continue
De plus en d’équipes pratiquent l’intégration continue. Team Build et Team Foundation Server sont particulièrement bien dotés pour cela :
Cependant, il peut être utile de contourner le lancement de la compilation lors d’un archivage ( édtion d’un script de build, création d’une branche…).
Lors de l’archivage de votre changeset, placez en tête de votre commentaire le texte suivant :
***NO_CI***
Cette commande indiquera au moteur de build que vous ne souhaitez pas déclencher l’intégration continue.
Mon équipe ne me quitte plus!!!
Les résultats des Builds de vos projets sur votre iPhone, c’est possible avec TFSToGo.
La partie configuration :

La liste des builds et le résultat :

Vais peut être changer de mobile moi…
Surveiller un serveur TFS
Basées sur mon expérience et les différentes installations que j’ai pu rencontré, voici mes recommandations concernant la supervision d’un serveur TFS. Celles ci ne sont pas exhaustives et doivent être majorées suivant votre topologie et les ressources matérielles de votre serveur. J’attends vos éventuels retours afin de préciser ce billet.
Quelque soit l’outil de supervision ou les procédures que vous voici une liste non-exhaustive des points que vous devez surveiller sur votre serveur TFS.
Configuration matérielle
Consommation CPU : dépend du nombre d’utilisateurs actuellement connectés. Une forte consommation CPU peut survenir lors de la génération du Warehouse et/ou de la consultation des rapports et de Sharepoint. Sur une heure, une consommation inférieure à 30% du CPU devrait représenter un comportement normal.
Consommation mémoire : Une forte consommation mémoire peut être observé sur une instance unique (AT + DT), cela est causé par SQL Server 2005 qui prend ses aises.
Espace disque : Dépend fortement de la volumétrie (nombres de projets d’équipe). Une partition système doit disposer d’au moins 4Go de libre afin d’accueillir les MSI de mise à jour, la partition de données ne devrait pas excéder 50% d’espace occupé.
Connectivité : les adresses IP de la machine sont elles celles attribuées lors de l’installation. Le nom de la machine est il celui d’origine.
IIS
Les sites Web suivants sont ils démarrés?
Default Web Site
Sharepoint Central Administration v3
Team Foundation Server
Team System Web Access
Les pools d’applications suivants sont ils démarrés?
Microsoft Team Foundation Server Application Pool
ReportServer
TFS WSS
DefaultAppPool
Sharepoint Central Administation V3
TswaPool
Vérifier les en-têtes d’hôte et les éventuels certificats .
Services Windows
Les services suivants sont ils démarrés?
TFSServerScheduler
SharePoint Timer Service
Journaux d’ événements
Surveiller toutes les entrées concernant Team Foundation Server bien sûr (Erreur TFXXXXXX) mais également celles liés à SQL Server et surtout à son agent (Sauvegardes), à Sharepoint et à Reporting Services. Vous pouvez également considérer tous les erreurs provoqués lors de l’exécution d’une application par les différentes comptes de services de TFS.
Les alertes concernant Team Foundation Version Control et Team Foundation WorkItem Tracking sont considérées comme critiques. Les alertes concernant TfsWarehouse et Reporting Services sont sérieuses.
SQL Server
Les services suivants sont ils démarrés?
Database Services
Analysis Services
Reporting Services
SQL Server Browser
SQL Server Agent
Les dernières exécutions des sauvegardes se sont elles bien déroulés?
Vérifiez la présence des fichiers et l’historique des jobs SQL Server.
KB947455 – Actions “Delete” répétées durant les fusions
Petit rappel : Une fusion dans Team Foundation Version Control est constituée d’une liste de “jeux de modifications” (ChangeSets en anglais) que l’on souhaite reporter d’une branche à l’autre. Les jeux de modifications comporte des actions (ajout, renommage, édition…) appliquées à des Items(les fichiers et les dossiers). La fusion consiste donc à répéter les différentes actions sur les items d’un jeu de modifications sur les items communs entre les branches.
Problème : Les actions de suppression des fichiers ne sont pas répétés durant les fusions dans TFVC 2005 et TFVC 2008. Les fichiers qui ont été supprimés dans la branche de destination seront donc toujours présents malgré le report de l’ensemble des modifications dans la branche Cible. Le problème est connu et une KB est disponible ici : http://support.microsoft.com/default.aspx/kb/947455/en-us
Résolution : Microsoft vient de publier un correctif visant à permettre de reporter les opérations de suppression à l’aide d’une opération de fusion. Ce correctif est disponible ici : http://code.msdn.microsoft.com/KB947455/Release/ProjectReleases.aspx?ReleaseId=1127 .
Il permet donc de voir apparaître des actions de suppression lors de la fusion entre deux branches.
Dans l’exemple ci-dessous, il s’agit du fichier web.config qui devra être supprimé après la fusion.
Nous avons bien l’effet attendu en vérifiant après la fusion les modifications en attentes sur l’espace de travail :
A noter toutefois que ce correctif ne permet pas de reporter des suppressions entre deux dossiers à l’aide de l’option /Baseless disponible à l’aide de l’outil en ligne de commande tf.exe, comme en témoigne le résultat des modifications en attentes ci-dessous.
Après exécution de la commande :
On constate donc que le fichier qui a été supprimé dans la branche source a donc été ignoré lors de la fusion.
Erreur 32000 lors de la migration de TFS 2008
Voici une entrée dans laquelle je vais présenter un problème que vous pourriez rencontrer lors d’une migration de votre serveur Team Foundation.

Bien que dans la majorité des migrations que nous effectuons, la procédure se résume à la simple exécution du programme d’installation, certaines petites manipulations ou configurations peuvent vous posait problèmes durant cette opération. Il semblerait que ce soit le cas pour la migration que j’ai opéré aujourd’***. Il s’agit d’une instance installée il y a de cela deux ans, qui n’a jamais posé de problème particulier hormis peut être avec Analysis Services. La remise en état du Warehouse se résumait à une reconstruction de l’entrepôt de données.
Voici donc l’erreur rencontrée durant la phase d’installation / migration:
Erreur 32000 : The Commandline ‘”C:Program FilesMicrosoft Visual Studio 2008 Team Foundation ServerToolsTfsDb.exe” upgrade /server:”SERVER” /property:”TFS_SERVICE_ACCOUNT=SERVERTFSSERVICE;TFS_REPORTING_ACCOUNT=SERVERTFSREPORTS;LCID=1033;VSTF_AS_INSTANCE=SERVER;VSTF_AS_DATABASE=TFSWarehouse” /showui:196682′ returned non-zero value: 100.
Cette erreur apparaît lors de la mise à jour de l’entrepôt de données, tiens, tiens…
La consultation des logs de l’installation présents dans les fichiers temporaires de l’utilisateur procédant à la migration nous révèle un premier indice :
Erreurs dans le gestionnaire de métadonnées. Une erreur s’est produite lors de l’instanciation d’un objet de métadonnées du fichier, « \?E:Program FilesMicrosoft SQL ServerMSSQL.2OLAPDataTFSWarehouse.0.dbToday.1452.dim.xml ».
En allant consulter le fichier indiqué dans le journal d’installation, j’ai pu constater qu’il s’agissait des informations de construction de la dimension Today du cube de Team Foundation Server. Dans ce fichier, qui semble être le fruit d’une sérialisation XML, l’erreur se comprend enfin : le fichier n’est pas valide, plus encore, il ne possède pas de balise de fin et n’est donc pas bien formé.
Après avoir pris conseil auprès de certains collègues plus au fait que moi dans les affaires de cubes et d’entrepôts, je prends mon courage à deux mains et renomme la dizaine de fichiers *.dim.xml qui semblent poser problèmes pour constater :
L’architecture de Team Foundation est venue à notre rescousse car l’entrepôt de données et le cube peuvent être regénérés uniquement à l’aide des données présentes dans les différentes bases relationnelles (les données opérationnelles de TFS). Le fichier WareHouseSchema.xml, présent dans le répertoire d’installation de TFS, contient l’intégralité des définitions nécessaires à sa reconstruction et bien entendu , nous n’aurions pas pu supprimer la définition des dimensions dans une définition standard d’un entrepôt.
En espérant que vous n’ayez pas à vous servir de ce post…



