web-dev-qa-db-fra.com

Quand est-il approprié de ne pas effectuer de test unitaire?

Je travaille dans une petite entreprise en tant que développeur solo. Je suis le seul développeur de l'entreprise en fait. J'ai plusieurs projets (relativement) importants que j'ai écrits et maintenus régulièrement, et aucun d'entre eux n'a de tests pour les soutenir. Alors que je commence de nouveaux projets, je me demande souvent si je devrais essayer une approche TDD. Cela semble être une bonne idée, mais honnêtement, je ne peux jamais justifier le travail supplémentaire impliqué.

Je travaille dur pour être avant-gardiste dans ma conception. Je me rends compte qu'un jour, un autre développeur devra certainement maintenir mon code, ou du moins le dépanner. Je garde les choses aussi simples que possible et je commente et documente des choses qui seraient difficiles à saisir. Et le fait est que ces projets ne sont pas si gros ou compliqués qu'un développeur décent aurait du mal à les comprendre.

Beaucoup d'exemples de tests que j'ai vus se résument aux détails, couvrant toutes les facettes du code. Étant donné que je suis le seul développeur et que je suis très proche du code dans l'ensemble du projet, il est beaucoup plus efficace de suivre un modèle de test d'écriture puis de test manuel. Je trouve également que les exigences et les fonctionnalités changent suffisamment fréquemment pour que la maintenance des tests ajoute une charge considérable à un projet. Temps qui pourrait autrement être consacré à la résolution des besoins de l'entreprise.

Je me retrouve donc avec la même conclusion à chaque fois. Le retour sur investissement est trop faible.

J'ai occasionnellement configuré quelques tests pour m'assurer que j'ai écrit un algorithme correctement, comme le calcul du nombre d'années qu'une personne a été dans l'entreprise en fonction de sa date d'embauche. Mais du point de vue de la couverture du code, j'ai couvert environ 1% de mon code.

Dans ma situation, trouveriez-vous toujours un moyen de faire des tests unitaires une pratique régulière, ou ai-je raison d'éviter ce surcoût?

MISE À JOUR: Quelques choses sur ma situation que j'ai laissées de côté: Mes projets sont tous des applications web. Pour couvrir tout mon code, je devrais utiliser des tests d'interface utilisateur automatisés, et c'est un domaine où je ne vois toujours pas un grand avantage par rapport aux tests manuels.

143
Ken Pespisa

Beaucoup d'exemples de tests que j'ai vus se résument aux détails, couvrant toutes les facettes du code.

Donc? Vous n'avez pas à tester tout. Juste les choses pertinentes.

Étant donné que je suis le seul développeur et que je suis très proche du code dans l'ensemble du projet, il est beaucoup plus efficace de suivre un modèle de test d'écriture puis de test manuel.

C'est en fait faux. Ce n'est pas plus efficace. C'est vraiment juste une habitude.

Ce que font les autres développeurs solo, c'est d'écrire un croquis ou un plan, d'écrire les cas de test, puis de remplir le plan avec le code final.

C'est très, très efficace.

Je trouve également que les exigences et les fonctionnalités changent suffisamment fréquemment pour que la maintenance des tests ajoute une charge considérable à un projet.

C'est faux aussi. Les tests ne sont pas la traînée. Les modifications des exigences sont la traînée.

Vous devez corriger les tests pour refléter les exigences. Que ce soit leurs minuties, ou de haut niveau; écrit en premier ou écrit en dernier.

Le code n'est pas fait jusqu'à ce que les tests réussissent. C'est la seule vérité universelle du logiciel.

Vous pouvez avoir un test d'acceptation limité "le voici".

Ou vous pouvez avoir des tests unitaires.

Ou vous pouvez avoir les deux.

Mais quoi que vous fassiez, il y a toujours un test pour démontrer que le logiciel fonctionne.

Je suggère qu'un peu de formalité et la suite d'outils de test unitaire Nice rendent ce test beaucoup plus utile.

85
S.Lott

Imaginez que vous disposiez d'une série de tests qui pourraient fonctionner en un clin d'œil et allumer une lumière verte ou rouge. Imaginez que cette suite de tests soit testée tout! Imaginez que tout ce que vous aviez à faire pour exécuter la suite de tests était de taper ^ T. Quel pouvoir cela vous donnerait-il?

Pourriez-vous apporter une modification au code sans craindre de casser quelque chose? Pourriez-vous ajouter une nouvelle fonctionnalité sans craindre de casser une ancienne fonctionnalité? Pourriez-vous nettoyer rapidement le code en désordre sans craindre de faire des dégâts?

Oui, tu pourrais faire toutes ces choses! Et qu'arriverait-il à votre code au fil du temps? Il deviendrait de plus en plus propre car il n'y aurait aucun risque à le nettoyer.

Imaginons que vous ayez une petite fée sur l'épaule. Chaque fois que vous écriviez une ligne de code, la fée ajoutait quelque chose à la suite de tests qui testait que cette ligne de code faisait ce qu'elle était censée faire. Donc, toutes les deux secondes, vous pouvez appuyer sur ^ T et voir que la dernière ligne de code que vous avez écrite fonctionne.

Combien de débogage pensez-vous que vous feriez?

Si cela ressemble à de la fantaisie, vous avez raison. Mais la réalité n'est pas très différente. Remplacez le regard par quelques secondes, et la fée avec la discipline TDD, et vous l'avez à peu près.

Disons que vous revenez à un système que vous avez construit il y a un an et que vous avez oublié comment créer l'un des objets centraux. Il existe des tests qui créent cet objet dans tous les sens où il peut être créé. Vous pouvez lire ces tests et vous rafraîchir la mémoire. Besoin d'appeler une API? Il existe des tests qui appellent cette API dans tous les sens. Ces tests sont peu documents, écrits dans une langue que vous comprenez. Ils sont totalement sans ambiguïté. Ils sont si formels qu'ils s'exécutent. Et ils ne peuvent pas se désynchroniser avec l'application!

Ne vaut pas l'investissement? Dis moi que c'est une blague! Comment pourrait-on NE PAS vouloir cette suite de tests? Faites-vous plaisir et arrêtez de chicaner sur la bêtise. Apprenez à bien faire le TDD et regardez à quelle vitesse vous allez et à quel point votre code est plus propre.

113
Uncle Bob.

L'erreur que vous faites est que vous voyez le test comme un investissement en temps sans retour immédiat. Cela ne fonctionne pas nécessairement comme ça.

Tout d'abord, écrire des tests vraiment vous concentre sur ce que cette partie de votre code doit faire.

Deuxièmement, leur exécution révèle des bogues qui pourraient autrement apparaître lors des tests.

Troisièmement, les exécuter affiche parfois des bogues qui ne se présenteraient pas autrement lors des tests et qui vous mordraient vraiment dans le cul en production.

Quatrièmement, si vous rencontrez un bug avec un système en cours d'exécution et créez un test unitaire pour celui-ci, vous ne pourrez pas réintroduire ce bug plus tard. Cela peut être d'une grande aide. Les bogues réintroduits sont courants et très ennuyeux.

Cinquièmement, si vous avez besoin de remettre du code à quelqu'un d'autre, une suite de tests vous facilitera la vie. De plus, si vous avez ignoré un projet et y revenez après quelques années, vous n'en serez plus si proche et cela vous sera également utile.

Mon expérience a toujours été qu'à travers le développement d'un projet, avoir des tests unitaires décents a toujours rendu le processus plus rapide et plus fiable.

34
glenatron

Les gars de JUnit (Java Unit Test Framework) ont une philosophie qui s'il est trop simple à tester, ne le testez pas . Je recommande fortement de lire leur FAQ sur les meilleures pratiques , car c'est assez pragmatique.

TDD est un processus différent d'écriture de votre logiciel. Le principe de base derrière les tests unitaires est que vous passerez moins de temps dans le débogueur à parcourir le code et à déterminer plus rapidement si votre changement de code casse accidentellement autre chose dans le système. Cela correspond à TDD. Le cycle TDD est comme ceci:

  1. Écrivez un test
  2. Regardez-le échouer (prouvez que vous avez quelque chose à faire)
  3. Écrivez juste ce qui est nécessaire pour réussir le test - pas plus.
  4. Regardez-le passer (yay!)
  5. Refactoriser (améliorer)
  6. Laver, rincer et répéter

Ce qui est moins évident dans l'application de TDD, c'est que cela change la façon dont votre code d'écriture . En vous forçant à réfléchir à la façon de tester/valider que le code fonctionne, vous écrivez du code testable. Et puisque nous parlons de tests unitaires, cela signifie généralement que votre code devient plus modulaire. Pour moi, le code modulaire et testable est une grande victoire dès le départ.

Maintenant, avez-vous besoin de tester des choses comme les propriétés C #? Imaginez une propriété définie comme ceci:

bool IsWorthTesting {get; set;}

La réponse serait "non", cela ne vaut pas la peine d'être testé, car à ce stade, vous testez la fonction de langue. Ayez juste confiance que les gars de la plateforme C # ont bien compris. De plus, s'il échouait, que pourriez-vous faire pour le réparer?

En outre, vous constaterez que certaines parties de votre code nécessiteront trop d'efforts pour être testées correctement. Cela signifie que ne le faites pas, mais assurez-vous de tester le code qui utilise/est utilisé par le problème délicat:

  • Les exceptions vérifiées ne peuvent se produire que si une installation a mal tourné. Java en a une tonne. Vous devez écrire un bloc catch ou déclarer l'exception vérifiée même s'il n'y a aucun moyen qu'il puisse échouer sans pirater les fichiers installés.
  • Les interfaces des utilisateurs. Trouver le contrôle sous test et appeler les bons événements pour simuler les actions d'un utilisateur sont très gênants et dans certains cas impossibles. Cependant, si vous utilisez le modèle Modèle/Vue/Contrôleur, vous pouvez vous assurer que votre modèle et vos contrôleurs sont testés et laisser la partie vue au test manuel.
  • Interactions client/serveur. Il ne s'agit plus d'un test unitaire, mais d'un test d'intégration . Écrivez toutes les parties qui vont jusqu'à l'envoi et la réception de messages sur le fil, mais ne passez pas réellement sur le fil. Une bonne approche consiste à réduire la responsabilité du code qui parle réellement sur le fil aux communications brutes. Dans votre code de test unitaire, simulez l'objet de communication pour vous assurer que les services se comportent comme prévu.

Croyez-le ou non, TDD vous aidera à adopter un rythme de développement durable. Ce n'est pas à cause de la magie, mais plutôt parce que vous avez une boucle de rétroaction serrée et que vous êtes capable de détecter rapidement des erreurs vraiment stupides. Le coût de la correction de ces erreurs est essentiellement constant (au moins suffisant pour la planification) car les petites erreurs ne deviennent jamais de grosses erreurs. Comparez cela avec la nature explosive des sprints de purge de code/débogage.

33
Berin Loritsch

Vous devez équilibrer le coût des tests avec le coût des bogues.

Écrire un test unitaire de 10 lignes pour une fonction qui ouvre un fichier, où l'échec est "fichier introuvable" est inutile.

Une fonction qui fait quelque chose de complexe à une structure de données complexe - alors évidemment oui.

Le bit délicat est entre les deux. Mais rappelez-vous que la valeur réelle des tests unitaires ne consiste pas à tester la fonction particulière, mais à tester les interactions délicates entre elles. Ainsi, un test unitaire qui détecte qu'un changement dans un bit de code, casse une fonction dans un module différent à 1000 lignes de distance, vaut son pesant de café.

24
Martin Beckett

Le test, c'est le jeu.

Créer un test est un pari que le coût des bogues dans une unité qui se produisent et ne pas les attraper avec ce test (maintenant et lors de toutes les futures révisions de code) est supérieur au coût de développement du test. Ces coûts de développement de test incluent des éléments tels que la paie pour l'ingénierie de test supplémentaire, le temps de mise sur le marché supplémentaire, les coûts d'opportunité perdus en ne codant pas d'autres choses, etc.

Comme tout pari, parfois vous gagnez, parfois vous perdez.

Parfois, un logiciel tardif avec beaucoup moins de bogues l'emporte sur des trucs rapides mais bogués qui arrivent en premier sur le marché. Parfois le contraire. Vous devez regarder les statistiques dans votre domaine particulier, et combien la direction veut jouer.

Certains types de bogues pourraient être si improbables ou être éliminés des premiers tests d'intégrité, qu'ils ne valent pas statistiquement le temps de créer des tests spécifiques supplémentaires. Mais parfois, le coût d'un bug est si élevé (médical, nucléaire, etc.) qu'une entreprise doit prendre un pari perdant (similaire à l'achat d'une assurance). De nombreuses applications n'ont pas un coût d'échec aussi élevé et n'ont donc pas besoin d'une couverture d'assurance non économique plus élevée. D'autres le font.

23
hotpaw2

Mon conseil est de tester uniquement le code que vous souhaitez travailler correctement.

Ne testez pas le code que vous souhaitez bugger et vous causer des problèmes en cours de route.

11
Nick Hodges

Je me demande souvent si je devrais essayer une approche TDD. Cela semble être une bonne idée, mais honnêtement, je ne peux jamais justifier le travail supplémentaire impliqué.

TDD et Unit Testing ne sont pas la même chose.

Vous pouvez écrire du code, puis ajouter des tests unitaires ultérieurement. Ce n'est pas TDD, et c'est beaucoup de travail supplémentaire.

TDD est la pratique du codage dans une boucle de lumière rouge. Lumière verte. Itérations de refactorisation.

Cela signifie écrire des tests pour du code qui n'existe pas encore, regarder les tests échouer, corriger le code pour que les tests fonctionnent, puis rendre le code "correct". Cela vous évite souvent de travailler

L'un des avantages du TDD est qu'il réduit le besoin de penser aux anecdotes. Des choses comme les erreurs de coupure disparaissent. Vous n'avez pas à parcourir la documentation de l'API pour savoir si la liste qu'elle renvoie commence à 0 ou 1, faites-le.

8
Paul Butcher

J'ai travaillé sur un système où nous avons testé presque tout. Les exécutions notables des tests ont été le PDF et code de sortie XLS.

Pourquoi? Nous avons pu tester les pièces qui ont collecté les données et construit le modèle qui a été utilisé pour créer la sortie. Nous avons également pu tester les parties qui ont déterminé quelles parties du modèle iraient aux fichiers PDF. Nous n'avons pas pu tester si le PDF = avait l'air correct parce que c'était totalement subjectif. Nous n'avons pas pu tester que toutes les parties d'un PDF étaient lisibles pour un tilisateur typique parce que c'était aussi subjectif Ou si le choix entre les graphiques à barres et à secteurs était correct pour l'ensemble de données.

Si la sortie va être subjective, il y a peu de tests unitaires, vous pouvez faire ce qui en vaut la peine.

3
sal

Pour beaucoup de choses, un "test d'écriture puis de test manuel" ne prend pas plus de temps que d'écrire quelques tests. Le gain de temps provient de la possibilité de réexécuter ces tests à tout moment.

Pensez-y: si vous avez une couverture de fonctionnalités décente avec vos tests (à ne pas confondre avec la couverture de code), et disons que vous avez 10 fonctionnalités - cliquer sur un bouton signifie que vous en avez environ 10 vous refaites vos tests ... pendant que vous vous asseyez et sirotez votre café.

Vous n'avez pas non plus pour tester les minuties. Vous pouvez écrire des tests d'intégration qui couvrent vos fonctionnalités si vous ne voulez pas descendre dans les moindres détails ... IMO, certains tests unitaires sont trop précis pour tester le langage et la plate-forme, et non le code.

TL; DR Ce n'est vraiment jamais approprié car les avantages sont tout simplement trop bons.

2
Steven Evers

Voici deux très bonnes réponses que j'ai trouvées:

  1. Quand effectuer un test unitaire ou un test manuel
  2. Quoi ne pas tester en matière de tests unitaires?

Les justifications pour éviter les frais généraux perçus:

  • Économie immédiate de temps/coûts pour votre entreprise
  • Économie potentielle de temps/d'argent dans le dépannage/la maintenabilité/l'extension à long terme même après votre départ.

Ne voudriez-vous pas laisser un excellent produit de votre côté comme preuve de la qualité de votre travail? Parlant en termes égoïstes, n'est-ce pas mieux pour vous que vous le fassiez?

2
Aditya P

Les développeurs professionnels écrivent des tests unitaires car, à plus long terme, ils gagnent du temps. Vous allez tester votre code tôt ou tard, et si vous ne le faites pas, vos utilisateurs le feront et si vous devez corriger des bugs plus tard, ils seront plus difficiles à corriger et auront plus d'effets.

Si vous écrivez du code sans tests et que vous n'avez pas de bogues, alors ça va. Je ne pense pas que vous puissiez écrire un système non trivial avec zéro bogue, donc je suppose que vous le testez d'une manière ou d'une autre.

Les tests unitaires sont également essentiels pour éviter les régressions lorsque vous modifiez ou refactorisez un code plus ancien. Ils ne prouvent votre changement n'a pas cassé l'ancien code mais ils vous donnent beaucoup de confiance (tant qu'ils passent bien sûr :))

Je ne voudrais pas revenir en arrière et écrire tout un lot de tests pour le code que vous avez déjà livré, mais la prochaine fois que vous devrez modifier une fonctionnalité, je vous suggère d'essayer d'écrire des tests pour ce module ou cette classe, obtenez une couverture jusqu'à 70% + avant d'appliquer les modifications. Voyez si cela vous aide.

Si vous l'essayez et pouvez honnêtement dire que ce n'était pas utile, alors assez juste, mais je pense qu'il y a suffisamment de preuves dans l'industrie pour aider à en faire au moins la peine tout en testant l'approche.

2
Steve

Il semble que la plupart des réponses soient pro-TDD, même si la question ne concernait pas le TDD mais les tests unitaires en général.

Il n'y a pas de règle complètement objective sur ce qui doit être testé ou non. Mais il y a quelques fois où il semble que de nombreux programmeurs ne testent pas les unités:

  1. Méthodes privées

Selon votre philosophie OOP, vous pouvez créer des méthodes privées pour dissocier des routines complexes de vos méthodes publiques. Les méthodes publiques sont généralement destinées à être appelées à de nombreux endroits différents et utilisées souvent, et les méthodes privées sont seulement vraiment appelé par une ou deux méthodes publiques dans une classe ou un module pour quelque chose de très spécifique. Il suffit généralement d'écrire des tests unitaires pour les méthodes publiques mais pas les méthodes privées sous-jacentes qui font que la magie opère. Si quelque chose ne va pas avec une méthode privée, vos tests unitaires de méthode publique devraient être suffisants pour détecter ces problèmes.

  1. Les choses que vous connaissez déjà devraient fonctionner (ou les choses testées par quelqu'un d'autre)

Beaucoup de nouveaux programmeurs vont à l'encontre de cela lorsqu'ils apprennent à tester, et pensent qu'ils doivent tester chaque ligne exécutée. Si vous utilisez une bibliothèque externe et que sa fonctionnalité est bien testée et documentée par ses auteurs, il est généralement inutile de tester la fonctionnalité spécifique dans des tests unitaires. Par exemple, quelqu'un peut écrire un test pour s'assurer que son modèle ActiveRecord persiste la valeur correcte pour un attribut avec un rappel "before_save" à la base de données, même si ce comportement lui-même est déjà soigneusement testé dans Rails. La ou les méthodes que le rappel appelle, peut-être, mais pas le comportement de rappel lui-même. Tout problème sous-jacent avec les bibliothèques importées serait mieux révélé par des tests d'acceptation plutôt que par des tests unitaires.

Les deux pourraient s'appliquer, que vous fassiez du TDD ou non.

1
Ravenstine

Ken, moi et de nombreux autres développeurs sommes arrivés à la même conclusion que vous à plusieurs reprises tout au long de notre carrière.

La vérité que je crois que vous trouverez (comme beaucoup d'autres) est que l'investissement initial d'écrire des tests pour votre application peut sembler intimidant, mais s'ils sont bien écrits et ciblés sur les bonnes parties de votre code, ils peuvent vraiment économiser une tonne de temps.

Mon gros problème était avec les frameworks de test disponibles. Je n'ai jamais vraiment senti qu'ils étaient ce que je cherchais, alors j'ai juste roulé ma propre solution très simple. Cela m'a vraiment aidé à me familiariser avec le "côté obscur" des tests de régression. Je vais partager un pseudo extrait de base de ce que j'ai fait ici, et j'espère que vous pourrez trouver une solution qui fonctionne pour vous.

public interface ITest {
    public string Name {
        get;
    }
    public string Description {
        get;
    }
    public List<ITest> SubTests {
        get;
    }
    public TestResult Execute();
}

public class TestResult {
    public bool Succesful {
        get;
        set;
    }

    public string ResultMessage {
        get;
        set;
    }

    private Dictionary<ITest, TestResult> subTestResults = new Dictionary<ITest, TestResult>();
    public Dictionary<ITest, TestResult> SubTestResults {
        get {
            return subTestResults;
        }
        set {
            subTestResults = value;
        }
    }
}

La seule partie délicate après cela consiste à déterminer le niveau de granularité qui, selon vous, est le meilleur "rapport qualité/prix" pour le projet que vous faites.

Construire un carnet d'adresses nécessitera évidemment moins de tests qu'un moteur de recherche d'entreprise, mais les fondamentaux ne changent pas vraiment.

Bonne chance!

0
Adam Carstensen