web-dev-qa-db-fra.com

Difficultés avec TDD & refactoring (ou - pourquoi cela est-ce plus douloureux que cela ne devrait être?)

Je voulais m'apprendre à utiliser l'approche TDD et j'ai eu un projet que j'avais voulu travailler pendant un moment. Ce n'était pas un grand projet, je pensais donc que ce serait un bon candidat pour TDD. Cependant, je me sens comme quelque chose qui 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 plus facilement des projets. Maintenant, je voulais aussi garder la logique commerciale pour cela comme découled de l'ONENOTE que possible au cas où j'ai décidé de construire mon propre stockage personnalisé et mon arrière-plan un jour.

Je suis d'abord commencé avec un test d'acceptation des mots de base des mots pour décrire ce que je voulais ma première fonctionnalité à faire. Cela ressemble à quelque chose comme ça (issu de la brièveté):

  1. Clics d'utilisateur Créer un projet
  2. Types d'utilisateurs dans le titre du projet
  3. Vérifiez que le projet est créé correctement

Sauter sur les affaires d'interface utilisateur et une planification intermédiaire, je viens à mon premier test de l'unité:

[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, refacteur, etc. Très bien, il a besoin de gagner des choses. Découpez certaines étapes ici, je vous retrouve.

[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 comment je prévois que cela ressemblerait.

Je vais sauter quelques étapes ici parce que ce post obtient suffisamment longtemps, mais j'ai suivi des processus similaires et, éventuellement, je reçois 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'ai essayé de le mettre en œuvre:

public String SaveNewProject(IProject project)
{
    Page projectPage = oneNoteInterop.CreatePage(...);
}

Et il y a le problème juste où le "..." est. Je me rends compte maintenant à ce stade que CreatePage nécessite un identifiant de section. Je ne me suis pas rendu compte là-dessus lorsque je pensais au niveau du contrôleur car je ne faisais que tester les bits pertinents pour le contrôleur. Cependant, tout le chemin ici, je me rends compte que je dois demander à l'utilisateur un emplacement de stocker le projet. Maintenant, je dois ajouter un ID d'emplacement au magasin de données, puis ajoutez-en un au projet, puis ajoutez-en un au contrôleur et ajoutez-le à tous les tests déjà écrits pour toutes ces choses. Il est devenu fastidieux très rapidement et je ne peux pas m'empêcher de m'empêcher de penser que j'aurais attrapé ce plus rapidement si j'ai esquissé la conception à l'avance plutôt que de la laisser être conçue lors du processus TDD.

Quelqu'un peut-il s'il vous plaît m'expliquer si j'ai fait quelque chose de mal dans ce processus? Y a-t-il de toute façon ce genre de refactoring peut être évité? Ou est-ce commun? S'il est courant, y a-t-il des moyens de le rendre plus indolore?

Merci a tous!

20
Landon

Bien que TDD soit (à juste titre) comme un moyen de concevoir et de développer votre logiciel, il est toujours judicieux de penser à la conception et à l'architecture à l'avance. IMO, "esquisse la conception à l'avance" est un jeu juste. Souvent, cela sera à un niveau plus élevé que les décisions de conception que vous serez conduit à 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 il y a des choses que vous pouvez faire pour rendre vos tests moins fragiles et minimiser la douleur.

  1. Autant que possible, gardez les détails de la mise en œuvre hors de vos tests. Cela signifie seulement tester les méthodes publiques et, dans la mesure du possible, basé sur l'état sur la vérification basée sur les interactions . 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.

  2. Minimiser la duplication dans votre code de test, tout comme vous le feriez dans le code de production. ce post est une bonne référence. Dans votre exemple, on dirait qu'il était douloureux d'ajouter la propriété ID sur 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 à une méthode ou d'initialiser une fois pour chaque test dans une méthode d'initialisation des tests.

19
jhewlett

... Je ne peux pas m'empêcher de me sentir comme si j'aurais attrapé ce plus rapidement si j'avais esquissé la conception à l'avance plutôt que de le laisser être conçu lors de la TDD Procles ...

Peut-être peut-être pas

D'une part, TDD a bien fonctionné, vous donnant des tests automatisés lorsque vous avez construit la fonctionnalité et vous enfreignez immédiatement lorsque vous deviez changer l'interface.

D'autre part, peut-être que si vous aviez commencé avec la fonction de haut niveau (sauvegarde) 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'auriez pas. C'est une expérience incontrôlable.

Mais si vous recherchez une leçon pour la prochaine fois: commencez au sommet. Et pensez à la conception autant que vous le souhaitez en premier.

10
Steven A. Lowe

https://frontendmasters.com/courses/angularjs-and-code-lestability/ à partir de 2:22:00 à la fin (environ 1 heure). Désolé que la vidéo ne soit pas gratuite, mais je n'ai pas trouvé de gratuit qui l'explique si bien.

L'une des meilleures présentations de la rédaction de code testable est dans cette leçon. C'est une classe angularjs, mais la partie de test est tout autour Java code, principalement parce que ce qu'il parle n'a rien à voir avec la langue et tout ce qui se tiendra avec l'écriture de bon code testable dans le première place.

La magie est en écrivant du code testtable, plutôt que de rédiger des tests de code. Il ne s'agit pas de l'écriture de code qui prétend être un utilisateur.

Il dépense également un peu de temps à écrire la spécification sous forme d'affirmations de test.

0
boatcoder