web-dev-qa-db-fra.com

Est-il normal de passer autant de temps, sinon plus, à écrire des tests que du code réel?

Je trouve les tests beaucoup plus difficiles et plus difficiles à écrire que le code réel qu'ils testent. Il n'est pas rare que je passe plus de temps à écrire le test que le code qu'il teste.

Est-ce normal ou est-ce que je fais quelque chose de mal?

Les questions " Les tests unitaires ou le développement piloté par les tests en valent-ils la peine? ", " Nous passons plus de temps à implémenter les tests fonctionnels qu'à implémenter le système lui-même, est-ce normal? " et leurs réponses sont plus sur la question de savoir si le test en vaut la peine (comme dans "devrions-nous sauter complètement les tests d'écriture?"). Bien que je sois convaincu que les tests sont importants, je me demande si passer plus de temps sur les tests que le code réel est normal ou si c'est seulement moi.

À en juger par le nombre de vues, de réponses et de votes positifs reçus à ma question, je ne peux que supposer que c'est une préoccupation légitime qui n'est abordée dans aucune autre question sur le site Web.

216
springloaded

Je me souviens d'un cours de génie logiciel, que l'un passe environ 10% du temps de développement à écrire du nouveau code, et les 90% restants sont le débogage, les tests et la documentation.

Étant donné que les tests unitaires capturent le débogage et testent l'effort dans du code (potentiellement automatisable), il serait logique que plus d'efforts soient consacrés à eux; le temps réel pris ne devrait pas être beaucoup plus que le débogage et les tests que l'on ferait sans écrire les tests.

Enfin, les tests devraient également servir de documentation! Il faut écrire des tests unitaires dans la manière dont le code est destiné à être utilisé; c'est-à-dire que les tests (et l'utilisation) doivent être simples, mettre les choses compliquées dans l'implémentation.

Si vos tests sont difficiles à écrire alors, le code qu'ils testent est probablement difficile à utiliser!

206
esoterik

C'est.

Même si vous ne faites que des tests unitaires, il n'est pas rare d'avoir plus de code dans les tests que le code réellement testé. Il n'y a rien de mal à cela.

Considérez un code simple:

public void SayHello(string personName)
{
    if (personName == null) throw new NullArgumentException("personName");

    Console.WriteLine("Hello, {0}!", personName);
}

Quels seraient les tests? Il y a au moins quatre cas simples à tester ici:

  1. Le nom de la personne est null. Une exception est-elle réellement levée? C'est au moins trois lignes de code de test à écrire.

  2. Le nom de la personne est "Jeff". Avons-nous "Hello, Jeff!" en réponse? C'est quatre lignes de code de test.

  3. Le nom de la personne est une chaîne vide. Quelle sortie attendons-nous? Quelle est la sortie réelle? Question secondaire: correspond-elle aux exigences fonctionnelles? Cela signifie quatre autres lignes de code pour le test unitaire.

  4. Le nom de la personne est suffisamment court pour une chaîne, mais trop long pour être combiné avec "Hello, " et le point d'exclamation. Que se passe-t-il? ¹

Cela nécessite beaucoup de tests de code. De plus, les morceaux de code les plus élémentaires nécessitent souvent un code de configuration qui initialise les objets nécessaires pour le code testé, ce qui conduit également souvent à écrire des talons et des maquettes, etc.

Si le rapport est très élevé, auquel cas vous pouvez vérifier quelques éléments:

  • Y a-t-il duplication de code entre les tests? Le fait qu'il s'agisse d'un code de test ne signifie pas que le code doit être dupliqué (copié-collé) entre des tests similaires: une telle duplication rendra la maintenance de ces tests difficile.

  • Existe-t-il des tests redondants? En règle générale, si vous supprimez un test unitaire, la couverture des succursales devrait diminuer. Si ce n'est pas le cas, cela peut indiquer que le test n'est pas nécessaire, car les chemins sont déjà couverts par d'autres tests.

  • Testez-vous uniquement le code que vous devez tester? Vous ne devez pas tester le framework sous-jacent des bibliothèques tierces, mais exclusivement le code du projet lui-même.

Avec les tests de fumée, les tests de système et d'intégration, les tests fonctionnels et d'acceptation et les tests de stress et de charge, vous ajoutez encore plus de code de test, donc vous ne devriez pas vous inquiéter d'avoir quatre ou cinq LOC ​​de tests pour chaque LOC de code réel.

Une note sur TDD

Si vous êtes préoccupé par le temps nécessaire pour tester votre code, il se peut que vous le fassiez mal, c'est-à-dire le code d'abord, les tests plus tard. Dans ce cas, TDD peut vous aider en vous encourageant à travailler par itérations de 15 à 45 secondes, en basculant entre le code et les tests. Selon les partisans de TDD, il accélère le processus de développement en réduisant à la fois le nombre de tests que vous devez faire et, plus important encore, la quantité de code métier à écrire et surtout la réécriture pour les tests.


¹ Soit n soit la longueur maximale d'une chaîne . Nous pouvons appeler SayHello et passer par référence une chaîne de longueur n - 1 qui devrait fonctionner très bien. Maintenant à Console.WriteLine étape, le formatage devrait se terminer par une chaîne de longueur n + 8, ce qui entraînera une exception. Peut-être, en raison des limites de la mémoire, même une chaîne contenant n /2 caractères entraînera une exception. La question à se poser est de savoir si ce quatrième test est un test unitaire (il en ressemble un, mais peut avoir un impact beaucoup plus important en termes de ressources par rapport aux tests unitaires moyens) et s'il teste le code réel ou le framework sous-jacent.

97
Arseni Mourzenko

Je pense qu'il est important de distinguer deux types de stratégies de test: les tests unitaires et les tests d'intégration/d'acceptation.

Bien que les tests unitaires soient nécessaires dans certains cas, ils sont souvent désespérément dépassés. Ceci est exacerbé par des métriques sans signification imposées aux développeurs, comme "100% de couverture". http://www.rbcs-us.com/documents/Why-Most-Unit-Testing-is-Waste.pdf fournit un argument convaincant pour cela. Tenez compte des problèmes suivants liés aux tests unitaires agressifs:

  • Une abondance de tests inutiles qui ne documentent pas une valeur commerciale, mais qui existent simplement pour se rapprocher de cette couverture à 100%. Là où je travaille, nous devons écrire des tests unitaires pour usines qui ne font que créer de nouvelles instances de classes. Cela n'ajoute aucune valeur. Ou ces longues méthodes .equals () générées par Eclipse - il n'est pas nécessaire de les tester.
  • Afin de faciliter les tests, les développeurs subdiviseraient les algorithmes complexes en unités testables plus petites. Sonne comme une victoire, non? Pas si vous devez ouvrir 12 classes pour suivre un chemin de code commun. Dans ces cas, les tests unitaires peuvent en fait réduire la lisibilité du code. Un autre problème avec cela est que si vous coupez votre code en morceaux trop petits, vous vous retrouvez avec une ampleur de classes (ou morceaux de code) qui semblent n'avoir aucune justification au-delà d'être un sous-ensemble d'un autre morceau de code.
  • La refactorisation de code hautement couvert peut être difficile, car vous devez également maintenir l'abondance de tests unitaires qui dépendent de son fonctionnement juste ainsi . Ceci est exacerbé par les tests unitaires comportementaux, où une partie de votre test vérifie également l'interaction des collaborateurs d'une classe (généralement simulée).

Les tests d'intégration/d'acceptation, d'autre part, sont une partie extrêmement importante de la qualité des logiciels, et d'après mon expérience, vous devriez consacrer beaucoup de temps à les faire correctement.

De nombreux magasins ont bu le TDD kool-aid, mais comme le montre le lien ci-dessus, un certain nombre d'études montrent que ses avantages ne sont pas concluants.

61
firtydank

Ne peut pas être généralisé.

Si j'ai besoin d'implémenter une formule ou un algorithme à partir d'un rendu basé physiquement, il se peut très bien que je passe 10 heures sur des tests unitaires paranoïaques, car je sais que le moindre bogue ou l'imprécision peut conduire à des bogues presque impossibles à diagnostiquer, des mois plus tard .

Si je veux simplement regrouper logiquement certaines lignes de code et lui donner un nom, uniquement utilisé dans la portée du fichier, je ne le testerai pas du tout (si vous insistez pour écrire des tests pour chaque fonction, sans exception, les programmeurs peuvent se replier écrire le moins de fonctions possible).

12
phresnel

Je trouve que c'est la partie la plus importante.

Les tests unitaires ne consistent pas toujours à "voir si cela fonctionne bien", mais à apprendre. Une fois que vous avez testé quelque chose d'assez, il devient "codé en dur" dans votre cerveau et vous finissez par réduire votre temps de test unitaire et vous pouvez vous retrouver à écrire des classes et des méthodes entières sans rien tester jusqu'à ce que vous ayez terminé.

C'est pourquoi l'une des autres réponses sur cette page mentionne que dans un "cours", ils ont fait des tests à 90%, car tout le monde avait besoin d'apprendre les mises en garde de leur objectif.

Les tests unitaires ne sont pas seulement une utilisation très précieuse de votre temps car ils améliorent littéralement vos compétences, c'est un bon moyen de revoir votre propre code et de trouver une erreur logique en cours de route.

4
Jesse

Oui, c'est normal si vous parlez de TDDing. Lorsque vous avez des tests automatisés, vous sécurisez le comportement souhaité de votre code. Lorsque vous écrivez d'abord vos tests, vous déterminez si le code existant a déjà le comportement souhaité.

Cela signifie que:

  • Si vous écrivez un test qui échoue, la correction du code avec la chose la plus simple qui fonctionne est plus courte que l'écriture du test.
  • Si vous écrivez un test qui réussit, vous n'avez pas de code supplémentaire à écrire, vous passez donc plus de temps à écrire le test que le code.

(Cela ne tient pas compte du refactoring de code, qui vise à passer moins de temps à écrire le code suivant. Il est équilibré par le refactoring de test, qui vise à passer moins de temps à écrire des tests ultérieurs.)

Oui aussi si vous parlez d'écrire des tests après coup, vous passerez plus de temps:

  • Déterminer le comportement souhaité.
  • Déterminer les moyens de tester le comportement souhaité.
  • Remplir les dépendances de code pour pouvoir écrire les tests.
  • Correction du code pour les tests qui échouent.

Que vous passerez réellement à écrire du code.

Alors oui, c'est une mesure attendue.

3
Laurent LA RIZZA

Est-il normal de passer autant de temps, sinon plus, à écrire des tests que du code réel?

Oui, ça l'est. Avec quelques mises en garde.

Tout d'abord, c'est "normal" dans le sens où la plupart des grands magasins fonctionnent de cette façon, donc même si cette façon était complètement erronée et stupide, le fait que la plupart des grands magasins fonctionnent de cette façon le rend "normal".

Je ne veux pas dire par là que le tester est mauvais. J'ai travaillé dans des environnements sans test et dans des environnements avec des tests obsessionnels compulsifs, et je peux toujours vous dire que même les tests obsessionnels compulsifs étaient meilleurs que pas de tests.

Et je ne fais pas encore TDD, (qui sait, je pourrais peut-être à l'avenir), mais je fais la grande majorité de mes cycles d'édition-exécution-débogage en exécutant les tests, pas l'application réelle, donc naturellement, je travaille beaucoup sur mes tests, afin d'éviter autant que possible d'avoir à exécuter l'application réelle.

Cependant, sachez qu'il existe des dangers dans les tests excessifs, et en particulier dans le temps passé à maintenir les tests. (J'écris principalement cette réponse afin de le souligner spécifiquement.)

Dans la préface de Roy Osherove The Art of Unit Testing (Manning, 2009) l'auteur reconnaît avoir participé à un projet qui n'a pas réussi à en grande partie en raison de l'énorme charge de développement imposée par les tests unitaires mal conçus qui devaient être maintenus pendant toute la durée de l'effort de développement. Donc, si vous vous retrouvez à passer trop de temps à ne rien faire mais à maintenir vos tests, cela ne ne signifie pas nécessairement que vous êtes sur la bonne voie, car c'est "normal". Votre effort de développement pourrait être entré dans un mode malsain, où une refonte radicale de votre méthodologie de test pourrait être nécessaire afin de sauver le projet.

2
Mike Nakis

Cela peut être pour beaucoup de gens, mais cela dépend.

Si vous écrivez d'abord des tests (TDD), il se peut que vous rencontriez un certain chevauchement dans le temps passé à écrire le test est en fait utile pour écrire le code. Considérer:

  • Détermination des résultats et des entrées (paramètres)
  • Conventions de nommage
  • Structure - où mettre les choses.
  • Pensée simple et ancienne

Lorsque vous écrivez des tests après avoir écrit le code, vous pouvez découvrir que votre code n'est pas facilement testable, donc écrire des tests est plus difficile/prend plus de temps.

La plupart des programmeurs ont écrit du code beaucoup plus longtemps que les tests, donc je m'attendrais à ce que la plupart d'entre eux ne soient pas aussi fluides. De plus, vous pouvez ajouter le temps nécessaire pour comprendre et utiliser votre cadre de test.

Je pense que nous devons changer notre état d'esprit sur le temps qu'il faut pour coder et comment les tests unitaires sont impliqués. Ne le regardez jamais à court terme et ne comparez jamais le temps total pour fournir une fonctionnalité particulière, car vous devez considérer non seulement que vous écrivez du code meilleur/moins bogué, mais aussi du code qui est plus facile à modifier et à le rendre encore plus performant. meilleur/moins buggy.

À un moment donné, nous ne sommes tous capables que d'écrire du code qui est si bon, donc certains des outils et des techniques ne peuvent offrir que beaucoup pour améliorer nos compétences. Ce n'est pas comme si je pouvais construire une maison si je n'avais qu'une scie à guidage laser.

2
JeffO

Est-il normal de passer autant de temps, sinon plus, à écrire des tests que du code réel?

  • Oui pour écrire tests unitaires (Tester un module isolément) si le code est fortement couplé (héritage, pas assez séparation des préoccupations , manquant injection de dépendance , pas développé par tdd )
  • Oui pour l'écriture tests d'intégration/d'acceptation si la logique à tester n'est accessible que via code gui
  • Non pour écrire des tests d'intégration/d'acceptation tant que le code gui et la logique métier sont séparés (Le test n'a pas besoin d'interagir avec l'interface graphique)
  • Non pour écrire des tests unitaires s'il y a une séparation des préoccupations, une injection de dépendance, le code a été développé par test (tdd)
1
k3b