Journée d'un développeur ALM : écriture d'un nouveau code pour un récit utilisateur
Publication: mars 2016
Vous êtes un nouvel utilisateur de Visual Studio Application Lifecycle Management (ALM) et Team Foundation Server (TFS) ? Sont vous vous demandez comment vous et votre équipe peuvent obtenir maximales bénéficiez de la version la plus récente de ces outils pour créer votre application ?
Puis prenez quelques minutes pour parcourir étape par étape grâce à ce didacticiel deux-chapitre et suivez une journée dans la vie de la pierre et Ariane, deux développeurs chez Fabrikam Fibre, une société fictive qui fournit des services connexes et télévision par câble. Vous verrez des exemples de la façon dont vous pouvez utiliser Visual Studio et TFS pour extraire et mettre à jour le code, suspendre le travail lorsque vous êtes interrompu, demander une révision du code, archiver vos modifications et effectuer d'autres tâches.
L'histoire à ce jour
L'équipe a commencé récemment adopter Visual Studio et Team Foundation Server pour Application Lifecycle Management (ALM). Ils configurent leur serveur et les ordinateurs clients, créés une file d'attente, planifié une itération et terminé autres nécessaires pour commencer à développer leur application de planification.
Vue d'ensemble de ce chapitre
Peter brièvement révise sa file d'attente et sélectionne la tâche qu'il fonctionnera sur aujourd'hui. Il écrit des tests unitaires pour le code qu'il prévoit de le développer. En général, il exécute les tests plusieurs fois en une heure, progressivement écrire des tests plus détaillés et ensuite écrire le code qui permet de les transmettent. Souvent, il présente l'interface de son code avec vos collègues qui utilisent la méthode qu'il écrit.
Notes
Les fonctionnalités de mon travail et couverture du Code sont abordées dans cette rubrique sont disponibles uniquement dans les Visual Studio Premiumet Visual Studio Ultimate.
Dans cette rubrique
Vérifier personnel en retard de traitement et préparer les tâches à commencer à travailler
Créer le premier test unitaire
Créer un stub pour le nouveau code
Exécuter le premier test
Acceptez l'API
Rouge, vert, refactoriser...
Couverture du code
Lorsque nous avons terminés ?
Archiver les modifications
Vérifier personnel en retard de traitement et préparer les tâches à commencer à travailler
Dans Team Explorer, Peter ouvre le Mon travail page. L'équipe a décidé que, au cours du sprint actuel, Peter fonctionneront sur évaluer un élément de priorité élevée en souffrance du produit, statut de la facture. Peter décide de commencer par implémenter des fonctions mathématiques, une tâche enfant de l'élément de file d'attente de priorité élevée. Il fait glisser cette tâche à partir de la Des éléments de travail disponibles de liste dans le des éléments de travail en cours & Modifications liste.
Pour examiner les personnel en retard de traitement et de préparer les tâches à commencer à travailler
Dans Team Explorer:
Si vous n'êtes pas déjà connecté au projet d'équipe que vous souhaitez utiliser, puis de se connecter au projet d'équipe.
Choisissez la maison, puis cliquez sur Mon travail.
Sur le Mon travail page, faites glisser la tâche à partir de la Des éléments de travail disponibles de liste à la Des éléments de travail en cours section.
Vous pouvez également sélectionner une tâche dans le Des éléments de travail disponibles de liste, puis cliquez sur Démarrer.
Projet de Plan de travail incrémentiel
Peter développe généralement du code dans une série de petites étapes. Chaque étape prend plu d'une heure en général et peut prendre au moins dix minutes. Dans chaque étape, il écrit un test unitaire et modifie le code qu'il développe pour qu'il passe le nouveau test, en plus des essais qu'il a déjà écrite. Parfois il écrit le nouveau test avant de modifier le code, et parfois il modifie le code avant d'écrire le test. Parfois il refactorise. Autrement dit, il améliore simplement le code sans ajouter de nouveaux tests. Il ne change jamais un test réussi, sauf s'il décide qu'il ne représente pas correctement un impératif.
À la fin de chaque étape de petite taille, qu'il s'exécute tous les tests d'unité sont utiles pour cette zone du code. Il ne considère pas l'étape terminée jusqu'à ce que chaque test réussi.
Toutefois, il ne vérifie pas le code dans Team Foundation Server jusqu'à ce qu'il a terminé la tâche entière.
Peter écrit vers le bas un plan approximatif pour cette succession de petites étapes. Il sait que les détails exacts et l'ordre de ceux d'une version ultérieure seront probablement modifiée comme il travaille. Voici sa liste initiale des étapes à suivre pour cette tâche particulière :
Créer un stub de méthode test — c'est-à-dire, uniquement la signature de la méthode.
Répondre à un cas typique.
Large gamme de test. Assurez-vous que le code répond correctement à une grande plage de valeurs.
Exception en cas de valeur négative. S'adapter avec des paramètres incorrects.
Couverture du code. Assurez-vous qu'au moins 80 % du code est exercé par les tests unitaires.
Certains de ses collègues d'écrire ce type de plan dans les commentaires dans leur code de test. D'autres mémoriser uniquement leur plan. Peter trouve qu'il est utile d'écrire sa liste d'étapes dans le champ Description de l'élément de travail tâche. Si il doit avoir basculer temporairement à une tâche plus urgente, il sait où trouver la liste lorsqu'il est en mesure d'y revenir.
Créer le premier test unitaire
Peter commence par créer un test unitaire. Il commence avec le test unitaire dans la mesure où il souhaite écrire un exemple de code qui utilise sa nouvelle classe.
C'est le premier test unitaire pour la bibliothèque de classes qu'il teste, il crée un nouveau projet de test unitaire. Il ouvre le Nouveau projet boîte de dialogue et choisit Visual C#, Test, puis Un projet de Test unitaire.
Le projet de test unitaire fournit un fichier C# dans lequel il peut écrire son exemple. À ce stade, il veut simplement illustrer comment une de ses nouvelles méthodes sera appelée :
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Fabrikam.Math.UnitTest
{
[TestClass]
public class UnitTest1
{
[TestMethod]
// Demonstrates how to call the method.
public void SignatureTest()
{
// Create an instance:
var math = new Fabrikam.Math.LocalMath();
// Get a value to calculate:
double input = 0.0;
// Call the method:
double actualResult = math.SquareRoot(input);
// Use the result:
Assert.AreEqual(0.0, actualResult);
}
}
}
Il a écrit l'exemple dans une méthode de test car, au moment où qu'il a écrit son code, il veut que l'exemple fonctionne.
Pour créer une unité de projet de test et les méthodes
Généralement, vous créeriez un projet de test pour chaque projet qui est en cours de test. S'il existe déjà un projet de test, vous pouvez ajouter les classes et les nouvelles méthodes de test.
Cette procédure utilise l'infrastructure de Test unitaire Visual Studio, mais vous pouvez également utiliser des structures d'autres fournisseurs. Test de works Explorer aussi bien avec les autres infrastructures, si vous installez la carte appropriée.
Créez un projet de Test, si elle n'existe pas déjà.
- Dans le Nouveau projet boîte de dialogue zone de, sélectionnez un langage tel que Visual Basic, Visual C++ ou Visual C#. Choisissez Test , puis un projet de Test unitaire.
Ajoutez vos tests pour la classe de test qui est fournie. Chaque test unitaire est une méthode.
Chaque test unitaire doit être préfixé par le TestMethodattribut et la méthode de test unitaire ne doivent avoir aucun paramètre. Vous pouvez utiliser n'importe quel nom que vous souhaitez pour une méthode de test unitaire :
[TestMethod] public void SignatureTest() {...}
<TestMethod()> Public Sub SignatureTest() ... End Sub
Chaque méthode de test doit appeler une méthode de la Assert(classe), pour indiquer si elle a réussi ou échoué. En général, vous vérifiez que les résultats attendus et réels d'une opération sont égales :
Assert.AreEqual(expectedResult, actualResult);
Assert.AreEqual(expectedResult, actualResult)
Vos méthodes de test peuvent appeler d'autres méthodes ordinaires qui n'ont pas la TestMethodattribut.
Vous pouvez organiser vos tests en plus d'une classe. Chaque classe doit être préfixé par le TestClassattribut.
[TestClass] public class UnitTest1 { ... }
<TestClass()> Public Class UnitTest1 ... End Class
Pour plus d'informations sur la façon d'écrire des tests unitaires en C++, consultez Écriture de tests unitaires pour C/C++ à l'aide de l'infrastructure de tests unitaires Microsoft pour C++.
Créer un stub pour le nouveau code
Ensuite, Peter crée un projet de bibliothèque de classes pour son nouveau code. Il existe désormais un projet pour le code en cours de développement et un projet pour les tests unitaires. Il ajoute une référence de projet à partir du projet de test dans le code en cours de développement.
Dans le nouveau projet, il ajoute la nouvelle classe et une version minimale de la méthode qui permettra au moins le test à exécuter correctement la génération. La méthode la plus rapide qui consiste à générer un stub de classe et de méthode à partir de l'appel dans le test.
public double SquareRoot(double p)
{
throw new NotImplementedException();
}
Pour générer des classes et des méthodes à partir des tests
Commencez par créer le projet dans lequel vous souhaitez ajouter la nouvelle classe, sauf si elle existe déjà.
Pour générer une classe
Placez le curseur sur un exemple de la classe que vous souhaitez générer, par exemple, LocalMath. Dans le menu contextuel, choisissez Générer le Code, Nouveau Type.
Dans le Nouveau Type boîte de dialogue, jeu de projet pour le projet de bibliothèque de classes. Dans cet exemple, il est Fabrikam.Math.
Pour générer une méthode
- Placez le curseur sur un appel à la méthode, par exemple, SquareRoot. Dans le menu contextuel, choisissez Générer le Code, Un Stub de méthode.
Exécuter le premier test
Peter génère et exécute le test en appuyant sur CTRL + R, T. Le résultat de test affiche un indicateur d'échec rouge et le test s'affiche sous la liste des Échec de Tests.
Il apporte une modification simple au code :
public double SquareRoot(double p)
{
return 0.0;
}
Il exécute le test à nouveau et il passe :
Pour exécuter des tests unitaires
Sur le Test menu, choisissez exécuter, Tous les Tests.
- ou -
Si l'Explorateur de tests est ouverte, choisissez Exécuter tous les.
- ou -
Placez le curseur dans un fichier de code de test et appuyez sur la touche CTRL + R, T.
Si un test apparaît sous Échec de Tests:
Ouvrez le test, par exemple, en double-cliquant sur le nom.
Le point à partir duquel le test n'a pas pu s'affiche.
Pour obtenir une liste complète de tests, choisissez Afficher tous les. Pour revenir au résumé, cliquez sur le HOME affichage.
Pour afficher les détails d'un résultat de test, sélectionnez le test dans l'Explorateur de tests.
Pour naviguer vers le code d'un test, double-cliquez sur le test dans l'Explorateur de tests, ou choisissez Ouvrez Test dans le menu contextuel.
Pour déboguer un test, ouvrir le menu contextuel pour un ou plusieurs tests et choisissez Déboguer les Tests.
Pour exécuter des tests dans l'arrière-plan chaque fois que vous générez la solution, bascule Exécuter les Tests après génération. Tests ayant précédemment échoué sont exécutés en premier.
Acceptez l'Interface
Peter appelle son collègue Julia sur Lync et partage son écran. Elle utilise son composant. Il illustre son initiale.
Julia pense que l'exemple est OK, mais les commentaires, « un grand nombre de fonctions passerait ce test ».
Réponses de Peter, "le premier test est juste pour nous assurer que les paramètres de la fonction et le nom sont corrects. Maintenant nous pouvons écrire de test qui capture l'exigence principale de cette fonction. »
Ensemble, ils écrivent le test suivant :
[TestMethod]
public void QuickNonZero()
{
// Create an instance to test:
LocalMath math = new LocalMath();
// Create a test input and expected value:
var expectedResult = 4.0;
var inputValue = expectedResult * expectedResult;
// Run the method:
var actualResult = math.SquareRoot(inputValue);
// Validate the result:
var allowableError = expectedResult/1e6;
Assert.AreEqual(expectedResult, actualResult, allowableError,
"{0} is not within {1} of {2}", actualResult, allowableError, expectedResult);
}
Conseil
Pour cette fonction, Peter est à l'aide de développement premier Test, dans lequel il écrit d'abord le test unitaire pour une fonctionnalité et puis écrit du code qui satisfait au test.Dans les autres cas, il constate que cette pratique n'est pas réaliste, au lieu de cela, qu'il écrit les tests une fois qu'il a écrit le code.Mais il estime très important d'écrire des tests unitaires, que ce soit avant ou après le code — car elles conservent le code stable.
Rouge, vert, refactoriser...
Peter suit un cycle dans lequel il a à plusieurs reprises écrit un test et confirme qu'elle tombe en panne, écrit du code pour rendre le test réussit, et considère ensuite la refactorisation — c'est-à-dire, améliorant le code sans modifier les tests.
Rouge
Peter appuie sur CTRL + R, T pour exécuter le test de nouveau qu'il a créé avec Ariane. Une fois qu'il a écrit n'importe quel test, qu'il s'exécute toujours pour vous assurer qu'il échoue avant qu'il écrit le code qui permet de passer. Il s'agit d'une pratique qu'il avait apprises après que qu'il a oublié de placer les assertions dans certains tests qu'il avait écrit. Voir le résultat de l'échec, qui lui donne confiance que lorsqu'il rend passer, le résultat du test correctement indique qu'une exigence a été satisfaite.
Une autre pratique utile consiste à définir Exécuter les Tests après génération. Cette option exécute les tests dans l'arrière-plan chaque fois que vous générez la solution, afin que vous ayez un rapport continu de l'état du test de votre code. Peter a été tout d'abord suspects qu'il peut être lent à répondre de Visual Studio, mais il constate que cela se produit rarement.
Vert
Peter écrit le code de la méthode qu'il développe sa première tentative :
public class LocalMath
{
public double SquareRoot(double x)
{
double estimate = x;
double previousEstimate = -x;
while (System.Math.Abs(estimate - previousEstimate) > estimate / 1000)
{
previousEstimate = estimate;
estimate = (estimate * estimate - x) / (2 * estimate);
}
return estimate;
}
Peter s'exécute à nouveau les tests et les tests réussis :
Refactoriser
Maintenant que le code effectue sa principale fonction, Peter examine le code pour trouver des moyens de rendre plus performantes, ou pour le rendre plus facile à modifier à l'avenir. Il se rend compte qu'il peut réduire le nombre de calculs effectués dans la boucle :
public class LocalMath
{
public double SquareRoot(double x)
{
double estimate = x;
double previousEstimate = -x;
while (System.Math.Abs(estimate - previousEstimate) > estimate / 1000)
{
previousEstimate = estimate;
estimate = (estimate + x / estimate) / 2;
//was: estimate = (estimate * estimate - x) / (2 * estimate);
}
return estimate;
}
Il vérifie que les tests réussissent toujours :
Conseil
Chaque modification que vous apportez lorsque vous développez le code doit être une refactorisation ou une extension :
-
Signifie que vous ne modifiez pas les tests car vous n'ajoutez pas de nouvelles fonctionnalités de refactorisation.
-
Extension : ajout de tests et apporter les modifications de code qui sont nécessaires pour réussir les tests existants et nouveaux.
Si vous mettez à jour code existant aux exigences qui ont été modifiés, vous supprimez également des tests anciens qui n'est plus représentent les prescriptions actuelles.
Évitez de modifier les tests qui ont déjà passé.En revanche, ajouter de nouveaux tests.Écrire des tests qui représentent un réel besoin.
Exécutez les tests après chaque modification.
... et répéter
Peter poursuit sa série d'extension et de la procédure, la refactorisation à l'aide de sa liste de petites étapes sous la forme d'un guide sommaire. Il n'effectue pas toujours une étape de refactorisation après chaque extension, et il effectue parfois plusieurs étapes de refactorisation successivement. Mais il s'exécute toujours les tests unitaires après chaque modification dans le code.
Parfois, il ajoute un test qui ne nécessite aucune modification du code, mais qui ajoute à sa confiance son code fonctionne correctement. Par exemple, il souhaite s'assurer que la fonction fonctionne sur un large éventail d'entrées. Il a écrit plus de tests, telle que la suivante :
[TestMethod]
public void SqRtValueRange()
{
LocalMath math = new LocalMath();
for (double expectedResult = 1e-8;
expectedResult < 1e+8;
expectedResult = expectedResult * 3.2)
{
VerifyOneRootValue(math, expectedResult);
}
}
private void VerifyOneRootValue(LocalMath math, double expectedResult)
{
double input = expectedResult * expectedResult;
double actualResult = math.SquareRoot(input);
Assert.AreEqual(expectedResult, actualResult, expectedResult / 1e6);
}
Ce test passe la première fois qu'il s'exécute :
Juste pour vous assurer que ce résultat n'est pas une erreur, il introduit temporairement une petite erreur dans son test pour le rendre échouent. Après avoir examiné la défaillance, il résout à nouveau.
Conseil
Toujours effectuer un test échoue avant de le rendre passer.
Exceptions
Peter maintenant se déplace l'écriture de tests pour les entrées exceptionnelles :
[TestMethod]
public void RootTestNegativeInput()
{
LocalMath math = new LocalMath();
try
{
math.SquareRoot(-10.0);
}
catch (ArgumentOutOfRangeException)
{
return;
}
catch
{
Assert.Fail("Wrong exception on negative input");
return;
}
Assert.Fail("No exception on negative input");
}
Ce test met le code dans une boucle. Il doit utiliser le Annuler bouton dans l'Explorateur de tests. Cela termine le code dans les 10 secondes.
Peter souhaite qu'une boucle sans fin ne peut pas se produire sur le serveur de build. Bien que le serveur impose un délai d'attente sur un texte complet exécuté, il est un très long délai d'attente et provoque un délai important. Par conséquent, il ajoute un délai d'expiration explicite pour ce test :
[TestMethod, Timeout(1000)]
public void RootTestNegativeInput()
{...
Le délai d'attente explicite fait échouer le test.
Peter met ensuite à jour le code pour traiter ce cas exceptionnel :
public double SquareRoot(double x)
{
if (x <= 0.0)
{
throw new ArgumentOutOfRangeException();
}
Régression
Le nouveau passes de test, mais il existe une régression. Échec d'un test qui permet de passer maintenant :
Peter trouve et corrige l'erreur :
public double SquareRoot(double x)
{
if (x < 0.0) // not <=
{
throw new ArgumentOutOfRangeException();
}
Une fois que c'est le cas, tous les tests réussissent :
Conseil
Assurez-vous que chaque passe de tests après chaque modification que vous apportez au code.
Couverture du code
A intervalles réguliers pendant son travail, et enfin avant qu'il vérifie dans le code, Peter Obtient un rapport de couverture du Code. Ce champ indique la quantité de code a exercé ses tests.
Équipe de Peter a pour but de couverture d'au moins 80 %. Ils assouplir cette exigence pour le code généré, car il peut être difficile d'obtenir une couverture élevée pour ce type de code.
Bonne couverture n'est pas une garantie que toutes les fonctionnalités du composant a été testée et il ne garantit pas que le code fonctionne pour chaque plage de valeurs d'entrée. Néanmoins, il existe une corrélation relativement étroite entre la couverture des lignes de code et la couverture de l'espace de comportement d'un composant. Par conséquent, une bonne couverture renforce la confiance l'équipe qu'ils testent la plupart du comportement qui leur convient.
Pour obtenir un rapport de couverture du code, sur le Tests menu, choisissez exécuter, Analyse de couverture du Code pour tous les Tests. Exécutez de nouveau tous les tests.
Peter Obtient une couverture totale de 86 %. Lorsqu'il s'étend au total dans le rapport, il montre que le code qu'il développe une couverture de 100 %. Il s'agit très satisfaisante, car la note importante est pour le code sous test. Les sections non couverts sont en fait dans les tests eux-mêmes. En activant/désactivant les Afficher Code couverture coloration bouton, Peter peut afficher les parties du code de test n'ont pas été testées. Toutefois, il décide que ces sections sont sans importance pour la couverture, car elles sont dans le code de test et ne seraient utilisés que si une erreur est détectée.
Pour vérifier qu'un test spécifique atteint dans des branches du code, vous pouvez définir Afficher Code couverture coloration , puis exécutez le test unique à l'aide de la exécuter commande dans le menu contextuel.
Lorsque nous avons terminés ?
Peter continue à mettre à jour le code dans les petites étapes jusqu'à ce qu'il est la preuve que :
Les tests toutes les unité disponibles.
Dans un projet avec un très grand jeu de tests unitaires, il peut être difficile pour un développeur d'attendre que tous les exécuter. Au lieu de cela, le projet opère un contrôlé à cocher service, dans laquelle tous les les tests automatisés sont exécutés pour chaque jeu de réservations archivé avant que cette dernière est fusionnée dans l'arborescence source. L'archivage du est rejeté en cas d'échec de l'exécution. Cela permet au développeur d'exécuter un jeu minimal de tests unitaires sur son propre ordinateur et puis continuer avec les autres travaux, sans courir le risque d'interrompre la génération. Pour plus d'informations, consultez Utiliser un processus de génération d'archivage contrôlé pour la validation des modifications.
Couverture du code est conforme à la norme de l'équipe. 75 % est une exigence de projet standard.
Ses tests unitaires simulent tous les aspects du comportement requis, y compris les entrées par défaut et exceptionnelles.
Son code est facile à comprendre et à étendre.
Lorsque tous ces critères sont réunis, Peter est prêt pour son code à contrôle de code source.
Principes du développement de code avec des tests unitaires
Peter applique les principes suivants lors du développement de code :
Développer des tests unitaires avec le code et de les exécuter fréquemment pendant le développement. Les tests unitaires représentent la spécification de votre composant.
Ne modifiez pas les tests unitaires, sauf si les exigences ont été modifiées ou les tests étaient incorrects. Ajouter progressivement de nouveaux tests comme étend la fonctionnalité du code.
Le but d'au moins 75 % de votre code doivent être couverts par les tests. Examinez les résultats de couverture du code à intervalles, et avant d'archiver le code source.
Archiver vos tests unitaires avec le code, afin qu'elles soient exécutées par les versions de serveur régulière ou permanente.
Si possible, pour chaque partie de la fonctionnalité, écrire que le test unitaire tout d'abord. Cela avant de développer le code qui satisfait à elle.
Archiver les modifications
Avant d'archiver ses modifications, Peter utilise à nouveau Lync pour partager son écran avec son collègue Julia afin qu'elle peut de manière informelle et interactive revoir avec lui qu'il a créés. Les tests continuent d'être l'objectif de leur discussion Julia étant principalement intéressée par ce que le code fait, pas son fonctionnement. Julia accepte que ce qui a écrit Peter répond à ses besoins.
Peter consigne toutes les modifications qu'il a effectué, y compris les tests et le code et les associe à la tâche qu'il a terminé. L'archivage en files d'attente système de génération d'équipe automatisés de l'équipe afin de valider ses modifications à l'aide de processus de génération de Build CI de l'équipe. Ce processus de génération permet de limiter les erreurs dans l'équipe leur codebase par génération et test — dans un environnement propre distinct à partir de leurs ordinateurs de développement, chaque modification que l'équipe fait.
Peter est averti lorsque la génération est terminée. Fenêtre, il voit que la génération a réussi et tous les tests passé dans les résultats de génération.
Pour archiver les modifications
Dans la barre de menus, cliquez sur mode, Team Explorer.
Dans Team Explorer, choisissez Home , puis cliquez sur Mon travail.
Sur le Mon travail page, choisissez Vérifier dans.
Passez en revue le contenu de la Les modifications en attente page pour vous assurer que :
Toutes les modifications pertinentes sont répertoriées dans Modifications inclus
Tous les éléments de travail pertinents sont répertoriés dans Les éléments de travail associés.
Spécifier une commentaire pour aider votre équipe à comprendre l'objectif de ces modifications lorsqu'elles consultent l'historique de contrôle de version des fichiers modifiés et des dossiers.
Choisissez Vérifier.
D'intégrer en permanence le code
Pour plus d'informations sur la définition d'un processus de génération de l'intégration continue, consultez Configurer une build CI. Une fois que vous avez défini ce processus de génération, vous pouvez choisir d'être averti des résultats des builds d'équipe.
Pour plus d'informations, consultez Exécuter, surveiller et gérer des builds.
Suivant (Suspend le travail résoudre un bogue et effectuer une révision du code)