Je voulais apprendre à utiliser l'approche TDD et j'avais un projet sur lequel je voulais travailler depuis un moment. Ce n'était pas un grand projet, donc j'ai pensé que ce serait un bon candidat pour TDD. Cependant, j'ai l'impression que quelque chose a mal tourné. Laissez-moi vous donner un exemple:
À un niveau élevé, mon projet est un complément pour Microsoft OneNote qui me permettra de suivre et de gérer des projets plus facilement. Maintenant, je voulais aussi garder la logique métier pour cela aussi découplée que possible de OneNote au cas où je déciderais de créer mon propre stockage personnalisé et back-end un jour.
J'ai d'abord commencé avec un test d'acceptation de mots simples pour décrire ce que je voulais que ma première fonctionnalité fasse. Cela ressemble à ceci (en le simplifiant par souci de concision):
- L'utilisateur clique sur créer un projet
- Types d'utilisateurs dans le titre du projet
- Vérifiez que le projet est créé correctement
En sautant les choses sur l'interface utilisateur et une planification intermédiaire, j'arrive à mon premier test unitaire:
[TestMethod]
public void CreateProject_BasicParameters_ProjectIsValid()
{
var testController = new Controller();
Project newProject = testController(A.Dummy<String>());
Assert.IsNotNull(newProject);
}
Jusqu'ici tout va bien. Rouge, vert, refactor, etc. Bon maintenant, il faut réellement enregistrer des trucs. Découpant quelques étapes ici, je termine avec cela.
[TestMethod]
public void CreateProject_BasicParameters_ProjectMatchesExpected()
{
var fakeDataStore = A.Fake<IDataStore>();
var testController = new Controller(fakeDataStore);
String expectedTitle = fixture.Create<String>("Title");
Project newProject = testController(expectedTitle);
Assert.AreEqual(expectedTitle, newProject.Title);
}
Je me sens toujours bien à ce stade. Je n'ai pas encore de magasin de données concret, mais j'ai créé l'interface à quoi je m'attendais.
Je vais sauter quelques étapes ici parce que ce message est assez long, mais j'ai suivi des processus similaires et finalement j'arrive à ce test pour mon magasin de données:
[TestMethod]
public void SaveNewProject_BasicParameters_RequestsNewPage()
{
/* snip init code */
testDataStore.SaveNewProject(A.Dummy<IProject>());
A.CallTo(() => oneNoteInterop.SavePage()).MustHaveHappened();
}
C'était bon jusqu'à ce que j'essaie de l'implémenter:
public String SaveNewProject(IProject project)
{
Page projectPage = oneNoteInterop.CreatePage(...);
}
Et il y a le problème là où se trouve le "...". Je me rends compte à ce stade que CreatePage nécessite un ID de section. Je ne m'en étais pas rendu compte lorsque je pensais au niveau du contrôleur parce que je ne voulais que tester les bits pertinents pour le contrôleur. Cependant, tout en bas ici, je me rends compte maintenant que je dois demander à l'utilisateur un emplacement pour stocker le projet. Maintenant, je dois ajouter un ID d'emplacement à la banque de données, puis en ajouter un au projet, puis en ajouter un au contrôleur et l'ajouter à TOUS les tests qui sont déjà écrits pour toutes ces choses. Il est devenu fastidieux très rapidement et je ne peux pas m'empêcher de penser que j'aurais compris cela plus rapidement si j'avais esquissé le design à l'avance plutôt que de le laisser être conçu pendant le processus TDD.
Quelqu'un peut-il m'expliquer si j'ai fait quelque chose de mal dans ce processus? Existe-t-il de toute façon ce type de refactoring qui peut être évité? Ou est-ce courant? S'il est courant, existe-t-il des moyens de le rendre plus indolore?
Merci a tous!
la source
Réponses:
Bien que TDD soit (à juste titre) présenté comme un moyen de concevoir et de développer votre logiciel, c'est toujours une bonne idée de penser à la conception et à l'architecture au préalable. IMO, "esquisser le design à l'avance" est un jeu équitable. Cependant, ce sera souvent à un niveau plus élevé que les décisions de conception que vous serez amené à travers TDD.
Il est également vrai que lorsque les choses changent, vous devrez généralement mettre à jour les tests. Il n'y a aucun moyen d'éliminer complètement cela, mais vous pouvez faire certaines choses pour rendre vos tests moins cassants et minimiser la douleur.
Autant que possible, gardez les détails de mise en œuvre hors de vos tests. Cela signifie uniquement tester par des méthodes publiques et, si possible, privilégier la vérification basée sur l'état à la vérification basée sur l'interaction . En d'autres termes, si vous testez le résultat de quelque chose plutôt que les étapes pour y arriver, vos tests devraient être moins fragiles.
Minimisez la duplication dans votre code de test, tout comme vous le feriez dans le code de production. Ce message est une bonne référence. Dans votre exemple, il semble difficile d'ajouter la
ID
propriété à votre constructeur, car vous avez appelé le constructeur directement dans plusieurs tests différents. Au lieu de cela, essayez d'extraire la création de l'objet dans une méthode ou de l'initialiser une fois pour chaque test dans une méthode d'initialisation de test.la source
Peut-être peut-être pas
D'une part, TDD fonctionnait très bien, vous offrant des tests automatisés au fur et à mesure que vous développiez des fonctionnalités et se cassant immédiatement lorsque vous deviez changer l'interface.
D'un autre côté, peut-être que si vous aviez commencé avec la fonctionnalité de haut niveau (SaveProject) au lieu d'une fonctionnalité de niveau inférieur (CreateProject), vous auriez remarqué des paramètres manquants plus tôt.
Là encore, peut-être que vous n'en auriez pas. C'est une expérience irremplaçable.
Mais si vous cherchez une leçon pour la prochaine fois: commencez par le haut. Et pensez au design autant que vous le souhaitez en premier.
la source
https://frontendmasters.com/courses/angularjs-and-code-testability/ D'environ 2:22:00 à la fin (environ 1 heure). Désolé que la vidéo ne soit pas gratuite, mais je n'en ai pas trouvé une gratuite qui l'explique si bien.
L'une des meilleures présentations de l'écriture de code testable se trouve dans cette leçon. C'est une classe AngularJS, mais la partie test est tout autour du code java, principalement parce que ce dont il parle n'a rien à voir avec le langage, et tout à voir avec l'écriture d'un bon code testable en premier lieu.
La magie réside dans l'écriture de code testable, plutôt que dans l'écriture de tests de code. Il ne s'agit pas d'écrire du code qui prétend être un utilisateur.
Il passe également un certain temps à rédiger la spécification sous la forme d'affirmations de test.
la source