Méthodologie: Écriture de tests unitaires pour un autre développeur

28

Je pensais au développement de logiciels et à l'écriture de tests unitaires. J'ai eu l'idée suivante:

Supposons que nous ayons des paires de développeurs. Chaque paire est responsable d'une partie du code. L'un de la paire implémente une fonctionnalité (écriture de code) et le second écrit un test unitaire pour cela. Les tests sont écrits après le code. Dans mon idée, ils s'entraident, mais travaillent plutôt séparément. Idéalement, ils travailleraient sur deux fonctionnalités de taille similaire, puis échangeraient pour la préparation du test.

Je pense que cette idée a quelques avantages:

  • les tests sont écrits par quelqu'un, qui peut en savoir plus sur la mise en œuvre,
  • le travail doit être fait un peu plus vite que la programmation en binôme (deux fonctionnalités en même temps),
  • les tests et le code en ont la personne responsable,
  • le code est testé par au moins deux personnes, et
  • peut-être que la recherche d'erreurs dans le code écrit par une personne qui teste votre code donnerait une motivation particulière pour écrire un meilleur code et éviter de couper les coins ronds.

Peut-être que c'est aussi une bonne idée d'ajouter un autre développeur pour la révision du code entre le code et le développement des tests.

Quels sont les inconvénients de cette idée? Est-il déjà décrit comme une méthodologie inconnue et utilisé dans le développement de logiciels?

PS. Je ne suis pas un chef de projet professionnel, mais je sais quelque chose sur les processus de développement de projet et je connais les quelques méthodologies les plus populaires - mais cette idée ne me semble pas familière.

franiis
la source
17
Vous décrivez simplement l'AQ en aval au niveau de l'unité. Si vous avez des paires de personnes travaillant sur quelque chose, avez-vous essayé la programmation de paires réelle avec TDD?
jonrsharpe
9
Cela fonctionnerait mieux si le rédacteur de test effectuait les tests en premier (écrit le code squelette) et que l'autre implémentait la fonctionnalité. Le premier contrôlerait la conception et l'autre ferait le gros du travail. Cela pourrait bien fonctionner si le premier sait ce qu'il fait et que le second ne veut pas suivre son exemple tout le temps. Je ne connais pas de nom pour ce mode de coopération. Je dirais .. revendiquez-le! Commencez à appeler ce développement Franiis.
Martin Maat
14
Cette critique n'a aucun sens et votre suggestion ne résout pas ce problème.
jonrsharpe
5
@franiis J'ai vu des collègues écrire assert truecomme des tests et l'appeler un jour parce que chaque test passait. Il manquait une étape importante: les tests devaient d'abord échouer, et devaient être réussis en changeant le code, pas les tests.
Eric Duminil
6
@franiis TDD est construit autour de l'itération. Ecrire un test ayant échoué. Écrivez le code qui rend le test vert. Refactor. Écrivez un test qui échoue. Écrivez le code qui rend le test vert. Refactor. Il semble que la partie "répéter jusqu'à ce que vous ayez des tests couvrant toutes vos exigences" vous manque. Mais le plus gros problème que vous semblez avoir est que les "tests" sont considérés comme quelque chose que vous devez avoir parce que quelqu'un l'a dit, plutôt que les tests étant un outil utile pour les développeurs . Si vous ne pouvez pas amener les gens à se soucier de la qualité (et de l'exactitude) de leur code, c'est votre problème, et c'est là que vous devriez commencer.
Luaan

Réponses:

30

L'approche générale consistant à utiliser des paires pour diviser l'effort d'écriture de code de production et d'écriture des tests unitaires associés n'est pas rare. Je me suis même personnellement associé de cette manière auparavant avec un succès décent. Cependant, une ligne stricte entre la personne qui écrit le code de production et la personne qui écrit le code de test ne donne pas nécessairement des résultats.

Lorsque j'ai utilisé une approche similaire, la paire commence par parler et obtenir une compréhension partagée du problème. Si vous utilisez TDD, vous pouvez commencer par quelques tests de base en premier. Si vous n'utilisez pas TDD, vous commencerez peut-être par la définition de la méthode. À partir de là, les deux membres du couple travaillent à la fois sur le code de production et le code de test, une personne se concentrant sur chaque aspect, mais discutant des moyens d'améliorer le code de production ainsi que le code de test derrière.

Je ne vois pas l'avantage de donner à chaque paire deux fonctionnalités. Vous vous retrouveriez avec quelque chose qui ressemble à TDD pour certaines fonctionnalités et quelque chose qui ne l'est pas pour d'autres fonctionnalités. Vous perdez votre concentration. Vous ne bénéficiez pas des avantages de l'examen par les pairs en temps réel. Vous ne bénéficiez d'aucun des principaux avantages de l'association.

La pratique de la programmation par paires n'est pas une question de vitesse, mais de qualité. Donc, essayer d'utiliser une technique modifiée en allant plus vite va à l'encontre de la nature. En créant des logiciels de meilleure qualité via la révision de code parallèle et le développement de tests, vous finissez par gagner du temps en aval car il y a au moins deux personnes qui connaissent chaque changement et vous éliminez (ou réduisez) les cycles d'attente pour la révision et le test par les pairs.

Thomas Owens
la source
Merci, mon idée suppose que les deux fonctionnalités sont développées de la même manière (mais les développeurs échangent des rôles) - juste pour clarifier, pas pour défendre le sens de ce concept. J'aime votre réponse et votre concentration sur la vitesse contre la qualité.
franiis
D'après mon expérience, les coûts de reprise l'emportent sur les avantages de cette approche. Je préférerais qu'une paire échange ces droits en utilisant le «ping-pong» ou une autre méthode.
neontapir
3
La pratique de la programmation par paires n'est pas une question de vitesse, mais de qualité. Pair TDD est une question de qualité, qui apporte une rapidité de réalisation, ce qui réduit les coûts de développement. C'est juste notre industrie qui apprend ce que les maçons savent depuis des milléniaux: votre mur sera mieux construit en moins de temps, avec moins d'effort et moins de frais si vous perdez un peu de temps à mettre en place une ligne de cordes et une règle de maçon, puis posez vos briques, que si vous posez votre brique et essayez de vous ajuster ensuite avec le niveau à bulle et le maillet. Et obtenez de l'aide avec les choses.
Laurent LA RIZZA
@LaurentLARIZZA Cela semble correct. Je suppose qu'une meilleure façon de le dire serait "La pratique de la programmation par paires ne concerne pas la vitesse dans le présent, mais la qualité et la vitesse dans le futur." Il s'agit certainement d'une pratique tournée vers l'avenir pour trouver les problèmes plus tôt, améliorer la robustesse du travail et partager les connaissances pour démolir les silos. Tous ces éléments ont un coût qui paiera souvent des récompenses à l'avenir.
Thomas Owens
@ThomasOwens: Eh bien, le coût de la qualité est seulement perçu, pas réel. Une fois votre test réussi (et vous avez rangé votre code), le scénario décrit par votre test est terminé et sécurisé, et vous avez l'assurance qu'il fonctionne comme prévu. C'est fait, et vous pouvez continuer. Si vous continuez sans avoir la certitude que le code fonctionne, vous venez d'accepter une dette que vous devrez effectuer les vérifications plus tard. Le coût des dettes, pas l'absence de dettes. Je veux dire, le "futur" dont vous parlez est dès que votre premier test réussit.
Laurent LA RIZZA
37

Le principal problème avec votre idée est que vous ne pouvez pas simplement écrire des tests pour n'importe quel code. Le code doit être testable.

C'est-à-dire que vous devez être en mesure d'injecter des simulacres, de séparer le bit que vous souhaitez tester, d'accéder à l'état modifié et à confirmer, etc.

À moins que vous n'ayez de la chance ou que vous n'écriviez d'abord le test, les chances d'écrire le test signifient réécrire un peu le code. Ce qui, si vous n'êtes pas la personne qui a écrit le code en premier lieu, va signifier des retards, des réunions, une refactorisation, etc.

Ewan
la source
Merci. mais la critique courante de TDD est que le code est parfois / souvent écrit pour rendre les tests "verts" - non pour être bon. Si les tests ne testent pas un aspect du code, il peut être omis dans le code. L'écriture d'un test plus tard pourrait aider cela (j'accepte que certaines modifications puissent être nécessaires après l'écriture du code, mais les développeurs devraient apprendre à écrire du code plus testable à l'avenir).
franiis
1
@frani est sûr, le principal problème n'est pas que vous écrivez les tests après, c'est la combinaison de faire cela et de ne pas être la même personne qui a écrit le code.
Ewan
mais si, par exemple, la programmation par paires serait moins longue. Si deux développeurs travaillent sur un terminal, ils ne peuvent en aucun cas travailler simultanément sur deux fonctionnalités, et mon idée devrait permettre cela (même dans une portée limitée). Les réunions de micro-équipe de 2 personnes ne devraient pas être un vrai fardeau.
franiis
25
@franiis: "Si les tests ne testent pas un aspect du code, alors il pourrait être omis dans le code." - C'est tout. Les tests sont un codage des exigences sous la forme d'exemples exécutables. S'il n'y a pas de test pour cela, alors il n'y a aucune exigence pour cela, et il ne devrait pas y avoir de code pour cela .
Jörg W Mittag
3
L'autre côté de ce que @ JörgWMittag a dit serait: si vos tests "ne testent pas certains éléments importants du code", alors vous devez corriger vos tests. Cela sera aussi vrai dans votre système que dans le TDD traditionnel.
bta
15

Le principal problème que je vois ici, au niveau de l'unité, lorsque j'écris du code, je veux le compiler, l' exécuter et supprimer les bugs les plus évidents immédiatement - même lorsque le code est incomplet et que je sais que l'unité, la fonction ou la fonction est seulement partiellement mis en œuvre. Et pour exécuter le code d'une unité, j'ai besoin d'un programme appelant l'implémentation, généralement un test unitaire ou au moins un test unitaire partiel. Il ne s'agit pas nécessairement du "style TDD à la livre", un tel test peut être écrit après ou avant le code testé.

Lorsqu'une version de mon unité est "fonctionnalité complète" et exempte de tout bogue, je peux la trouver par moi-même, alors il est logique de la remettre à une deuxième personne et de la laisser écrire des tests unitaires supplémentaires ou revoir mon code . Mais pour moi, cela n'a aucun sens de le remettre dès que le compilateur n'affiche aucun avertissement, c'est définitivement trop tôt au cas où je sais que je devais expliquer en détail au testeur des choses qui ne fonctionnent pas "encore", ou fonctionneront différemment en deux heures puisque je travaille toujours sur ce morceau de code. Les frais généraux de communication nécessaires à ce niveau de détail ne seraient pas compensés par les avantages à mon humble avis.

Alors oui, avoir un deuxième développeur écrivant des tests unitaires supplémentaires est logique, mais pas pour écrire les tests unitaires exclusivement .

Doc Brown
la source
7

Il semblerait possible que l'une des situations suivantes se produise, toutes indésirables:

Confusion

Comme l'a souligné Ewan, la CUT pourrait devoir être modifiée pour la rendre testable. La raison du changement n'est pas toujours évidente pour le développeur (et peut provoquer un désaccord), c'est pourquoi les tests sont écrits en premier.

Contention

Le développeur A a peut-être terminé son code et souhaite le tester. Le développeur B peut également être en développement et peut donc être réticent à garer son code pour assister aux tests unitaires.

Changement de contexte

Même si le développeur B est disposé à suspendre son développement pour tester le code écrit par le développeur A - le changement d'activité a un coût.


Il est admis depuis des décennies que doubler la puissance humaine ne réduit pas de moitié le temps de développement. Compte tenu des facteurs que j'ai décrits ci-dessus, il est difficile de voir comment cet arrangement améliorerait les choses.

Robbie Dee
la source
4

Lorsqu'il est utilisé en conjonction avec la programmation par paires et TDD, cela s'appelle Ping Pong Pattern :

  • A écrit un nouveau test et constate qu'il échoue.
  • B implémente le code nécessaire pour réussir le test.
  • B écrit le test suivant et constate qu'il échoue.
  • A implémente le code nécessaire pour réussir le test.

Etc. Le refactoring se fait chaque fois que le conducteur en a besoin.

Mais vous semblez proposer que les deux programmeurs codent avec des ordinateurs différents. Le faire séparément nécessiterait d'avoir une spécification de très bas niveau. Cela va à l'encontre des métodologies agiles. Chaque changement devrait être coordonné. Dans TDD, vous faites la conception de bas niveau à la volée et ce n'est pas un problème. Je suppose que votre approche nécessiterait d'avoir une sorte de squelette déjà codé.

Quoi qu'il en soit: vous pouvez en apprendre beaucoup en testant de nouvelles façons de faire, même si elles ne sont pas efficaces à 100%. Vous pouvez le tester et partager votre expérience réelle

Borjab
la source
3

Je viens en retard à cette fête, mais je pense que j'ai quelque chose à ajouter.

Est-il déjà décrit comme une méthodologie inconnue et utilisé dans le développement de logiciels?

Vous décrivez le test par les pairs .

Supposons que nous ayons des paires de développeurs.

Ah, bonne vieille programmation par paires .

Chaque paire est responsable d'une partie du code. L'un de la paire implémente une fonctionnalité (écriture de code) et le second écrit un test unitaire pour cela. Les tests sont écrits après le code. Dans mon idée, ils s'entraident, mais travaillent plutôt séparément.

Ce n'est pas de la programmation par paires.

Idéalement, ils travailleraient sur deux fonctionnalités de taille similaire, puis échangeraient pour la préparation du test.

C'est certainement un test par les pairs. Voici un article ACM à ce sujet . J'ai fait ça. J'ai travaillé là où cela faisait officiellement partie du processus d' examen par les pairs . C'est utile, mais ce n'est certainement pas censé être la première ligne de test, et ce n'est certainement pas la programmation par paires classique.

Un autre nom pour cela est Whitebox Testing . Bien que cette définition ne se préoccupe pas autant de qui fait les tests que du fait que le testeur voit le fonctionnement interne de la chose qu'il teste, par opposition aux tests de la boîte noire où ils ne voient que ce qui se passe et ce qui sort. La boîte noire est généralement ce que fait QA.

La première ligne de test repose fermement entre les mains du codeur. Si ce n'est pas le cas, vous me demandez de ne pas tester moi-même mon code, ce que je refuse catégoriquement. Je teste mon code depuis l'âge de 10 ans. Je n'ai peut-être pas testé avec des tests unitaires sophistiqués à l'époque, mais mon code a été testé. Il a été testé chaque fois que je l'ai exécuté.

Ce que j'attends d'un testeur de pairs, ce sont des tests qui s'ajoutent à mes tests. Des tests qui clarifient en profondeur les problèmes rencontrés par le pair avec le code lorsqu'ils l'ont examiné. En exprimant ces problèmes avec un test automatisé, il est beaucoup plus facile de comprendre ce qu'ils signifient. En fait, j'ai eu des conversations techniques avec des pairs qui ne pouvaient tout simplement pas voir mon point de vue, puis j'ai réalisé que la meilleure façon de leur montrer le problème était d'écrire un test unitaire. C'est le test par les pairs.

Maintenant, si vous voulez me donner des tests écrits avant d'écrire correctement mon code. Rien de tel qu'un document d'exigences si formel qu'il compile.

candied_orange
la source
Merci de votre réponse et en m'indiquant les tests par les pairs (je vais lire à ce sujet).
franiis
1

J'ai fait du DDT (tests axés sur le développement, alias. Tests après le code), la programmation de paires et le TDD red-green-refactor pendant plusieurs années chacun. Pour répondre point par point à vos affirmations:

les tests sont écrits par quelqu'un, qui peut en savoir plus sur la mise en œuvre

La personne qui écrit des tests doit connaître l'implémentation le plus intimement possible, pour écrire des tests avec une bonne couverture sans sur-tests. L'exemple classique est de tester avec trois entrées lorsque deux prouveraient ce que vous essayez de tester. Bien qu'ils puissent se familiariser à la surface avec le code en le lisant, ils ne seront pas en mesure de comprendre exactement ce que le développeur d'origine a traversé pour arriver à l'état actuel. Ils auront donc une compréhension moins qu'optimale du code.

le travail doit être fait un peu plus rapidement que la programmation en binôme (deux fonctionnalités en même temps)

Je ne comprends pas pourquoi tu dis ça. Pendant que quelqu'un écrit des tests, il ne travaille pas sur de nouvelles fonctionnalités. Vous ne pouvez pas par magie doubler la capacité de travail d'une personne en lui donnant deux types de travail différents. D'après mon expérience, l'écriture de tests est généralement plus difficile que l'écriture de code de production, vous ne pouvez donc certainement pas travailler de manière productive et responsable sur des tests pour du code tout en écrivant une autre fonctionnalité.

les tests et le code ont une personne responsable

Tout d'abord, les tests sont du code. Pour le code de test d'entreprise est presque aussi important que le code de production, car il permet à l'entreprise de changer le logiciel sans crainte. Deuxièmement, ce n'est pas différent d'une personne écrivant les tests et le code de production, ou même une paire écrivant les deux.

le code est testé par au moins deux personnes

Non, c'est seulement testé par la personne qui écrit le test. À moins que vous ne vouliez consacrer encore plus de temps aux tests, dans ce cas, pourquoi s'arrêter à deux?

peut-être que la recherche d'erreurs dans le code écrit par une personne qui teste votre code donnerait une motivation particulière pour écrire un meilleur code et éviter de couper les coins ronds.

Les développeurs (même les seniors) ont des idées très différentes de ce qui constitue un "bon" code. Le coup de coin d'une personne est le moyen parfaitement valide d'une autre pour arriver au code de travail dès que possible. C'est une recette pour blâmer et pour jouer au système.

Red-green-refactor TDD (en fait écrire un seul test avant d'écrire du code de production, de l'exécuter, de le voir échouer, de modifier le code de production uniquement, de relancer le test, de le voir réussir, puis de le refactoriser, et de ne pas ignorer ou échanger l'un des ces étapes) et les revues de code fonctionnent.

l0b0
la source
Ce serait plus rapide (vraisemblablement) parce que vous n'avez pas deux personnes qui font le "même travail" - elles font chacune leur propre chose, puis changent à mi-chemin.
Jacob Raihle
@JacobRaihle Pairing n'est pas deux personnes qui se développent côte à côte sans aucune communication. Ce serait deux personnes faisant le même travail. Le jumelage est vraiment efficace car deux personnes collaborent sur un travail. D'après mon expérience, le développement est à peu près aussi rapide que pour les programmeurs individuels (c'est-à-dire que les paires font le travail deux fois plus vite), le logiciel résultant est de bien meilleure qualité et les connaissances ont été partagées.
l0b0
J'essaie d'expliquer la raison d'être du «travail devrait être fait un peu plus vite», ce qui vous a semblé confus. Le jumelage est généralement plus lent dans mon expérience, bien que je pense toujours que cela en vaut la peine (préférable à la fois au travail individuel et aux tests de transfert des OP). Si c'est plus rapide pour vous, tant mieux.
Jacob Raihle
1

Je pense que cette idée a quelques avantages:

Le'ts les parcourent un par un.

les tests sont écrits par quelqu'un, qui peut en savoir plus sur la mise en œuvre,

Donc, vous voulez dire que le premier développeur a passé du temps à écrire une implémentation, dont il n'est pas sûr qu'elle fonctionne. Ensuite, un autre développeur vient et écrit des tests, basant son raisonnement sur du code que personne ne sait s'il est correct, et en espérant qu'il apporte un avantage tactique par rapport à l'écriture de tests uniquement en ce qui concerne ce que le code est censé faire. Si l'implémentation est incorrecte, mon avis sera qu'elle n'apporte aucune aide à l'écriture des tests.

le travail doit être fait un peu plus rapidement que la programmation en binôme (deux fonctionnalités en même temps)

Une fois que les deux développeurs ont terminé leur développement initial, personne ne sait si l'un ou l'autre de leurs codes est correct. Cela reste à vérifier, personne ne peut cocher qui que ce soit comme fait, et personne ne peut prédire quand cela sera fait. Comparez cela à TDD: vous écrivez d'abord le test, puis vous échouez, puis vous passez avec le code. C'est du code qui prend en charge de plus en plus de scénarios. C'est une motion avancée.

Si vous les faites progresser en parallèle, le code qui pourrait être réutilisé dans les deux fonctionnalités sera écrit deux fois et coûtera deux fois plus.

les tests et le code en ont la personne responsable,

Examinez la propriété collective du code, comme proposé par XP. Vous aurez encore plus de personnes responsables du code. Si votre objectif est de partager les connaissances entre les développeurs, pourquoi essayez-vous de les séparer?

le code est testé par au moins deux personnes

Avec paire TDD aussi. Lors du couplage, les deux personnes doivent convenir que le code écrit est adéquat ou ne pas l'écrire. Si cela se traduit par un combat, certaines personnes de l'équipe ont un problème d'ego mal placé.

peut-être que la recherche d'erreurs dans le code écrit par une personne qui teste votre code donnerait une motivation particulière pour écrire un meilleur code et éviter de couper les coins ronds.

La recherche d'erreurs implique qu'à un moment donné, vous avez toléré qu'ils pénètrent. S'ils sont entrés, ils n'ont pas été remarqués. Refuser d'écrire des tests en premier, c'est autoriser les erreurs à entrer.

Le coin de coupe peut être involontaire. C'est à cela que sert la programmation par paires. Chaque membre de la paire devrait être chargé de ne pas laisser l'autre couper les coins ronds, parce que nous le faisons tous. Cela nécessite de laisser votre fierté dans le placard et de le reprendre lorsque vous quittez le bureau. Si vous vous attendez à ce que votre personnel soit infailliblement rigoureux, vous ne pensez pas à la situation courante et vous vous préparez à l'échec.

XP dit explicitement que toutes les pratiques XP sont faites pour se renforcer mutuellement en couvrant les défauts des autres. Vous ne devez pas écouter les critiques de toute pratique de XP séparée des autres. Aucune pratique n'est parfaite, TDD n'est pas parfait, la programmation par paires n'est pas parfaite, la propriété collective de code n'est pas parfaite, mais ils se couvrent tous les uns les autres.

Laurent LA RIZZA
la source