Comment testez-vous un test unitaire? [fermé]

89

Je regardais les webémissions de Rob Connerys sur l'application MVCStoreFront, et j'ai remarqué qu'il testait unitaire même les choses les plus banales, des choses comme:

public Decimal DiscountPrice
{
   get
   {
       return this.Price - this.Discount;
   }
}

Aurait un test comme:

[TestMethod]
public void Test_DiscountPrice
{
    Product p = new Product();
    p.Price = 100;
    p.Discount = 20;
    Assert.IsEqual(p.DiscountPrice,80);
}

Alors que je suis tout à fait pour les tests unitaires, je me demande parfois si cette forme de premier développement de test est vraiment bénéfique, par exemple, dans un processus réel, vous avez 3-4 couches au-dessus de votre code (demande commerciale, document d'exigences, document d'architecture) , où la règle métier réelle définie (le prix de remise est le prix - la remise) peut être mal définie.

Si tel est le cas, votre test unitaire ne vous dit rien.

De plus, votre test unitaire est un autre point d'échec:

[TestMethod]
public void Test_DiscountPrice
{
    Product p = new Product();
    p.Price = 100;
    p.Discount = 20;
    Assert.IsEqual(p.DiscountPrice,90);
}

Maintenant, le test est défectueux. Évidemment, dans un simple test, ce n'est pas grave, mais disons que nous testions une règle métier compliquée. Que gagnons-nous ici?

Avance rapide de deux ans dans la vie de l'application, lorsque les développeurs de maintenance la maintiennent. Maintenant que l'entreprise change sa règle, et le test se brise à nouveau, un développeur débutant corrige alors le test de manière incorrecte ... nous avons maintenant un autre point d'échec.

Tout ce que je vois, ce sont plus de points d'échec possibles, sans réel retour bénéfique, si le prix réduit est erroné, l'équipe de test trouvera toujours le problème, comment les tests unitaires ont-ils sauvé du travail?

Qu'est-ce que j'oublie ici? S'il vous plaît, apprenez-moi à aimer TDD, car j'ai du mal à l'accepter comme utile jusqu'à présent. Je veux aussi, parce que je veux rester progressiste, mais cela n'a pas de sens pour moi.

EDIT: Quelques personnes n'arrêtent pas de mentionner que les tests aident à appliquer la spécification. D'après mon expérience, les spécifications sont également erronées, le plus souvent, mais peut-être que je suis condamné à travailler dans une organisation où les spécifications sont écrites par des personnes qui ne devraient pas écrire de spécifications.

FlySwat
la source
5
dans de nombreux cas, le test unitaire est la spécification, et la documentation aussi!
Steven A. Lowe
32
... puis test unitaire le test unitaire du test unitaire ... mais qu'en est-il du test unitaire ^ 4 et du test unitaire ^ 5 ... aaaaaaaaahhhhhhhhh!
dacracot
14
Aucun test de quelque sorte que ce soit ne vous évitera une mauvaise spécification.
Bill the Lizard
4
Quelqu'un d'autre vient-il d'entendre ce qui ressemblait à un applaudissement d'une main?
gnovice le
9
Je pense que la citation appropriée est «Ce ne sont que des tortues tout en bas».
Quinn Taylor

Réponses:

63

Premièrement, les tests sont comme la sécurité - vous ne pouvez jamais être sûr à 100% de l'avoir, mais chaque couche ajoute plus de confiance et un cadre pour résoudre plus facilement les problèmes qui subsistent.

Deuxièmement, vous pouvez diviser les tests en sous-programmes qui peuvent eux-mêmes être testés. Lorsque vous avez 20 tests similaires, créer un sous-programme (testé) signifie que votre test principal consiste en 20 appels simples du sous-programme, ce qui est beaucoup plus susceptible d'être correct.

Troisièmement, certains diront que TDD répond à cette préoccupation. Autrement dit, si vous n'écrivez que 20 tests et qu'ils réussissent, vous n'êtes pas complètement convaincu qu'ils testent réellement quoi que ce soit. Mais si chaque test que vous avez écrit initialement a échoué , puis que vous l'avez corrigé, vous êtes beaucoup plus sûr qu'il teste vraiment votre code. À mon humble avis, ce va-et-vient prend plus de temps qu'il n'en vaut la peine, mais c'est un processus qui tente de répondre à votre préoccupation.

Jason Cohen
la source
2
Pour jouer l'avocat du diable, je vois des couches supplémentaires comme plus de points d'échec possibles, cela n'augmente pas la confiance en moi. Dans mon vrai travail, je travaille avec de nombreuses équipes au sein d'une entreprise SOA hautement distribuée. Chacune de ces équipes pourrait compromettre le projet si leur couche échoue.
FlySwat
2
C'est pourquoi vous utilisez des objets fictifs pour tester chaque couche séparément.
Toon Krijthe
10
Super, donc mon test passera en utilisant IMockedComplexObject mais quand j'utilise réellement un ComplexObject dans le monde réel, il échoue ... je n'ai plus rien gagné.
FlySwat
16
@Jonathan - non, vous avez acquis la certitude que votre code fonctionne - en supposant que vous avez développé l'interface de ComplexObject et que vous l'avez testé de manière adéquate par rapport à cette interface. Au pire, vous avez appris que votre compréhension de ComplexObject n'était pas ce que vous aviez prévu.
tvanfosson
9
@FlySwat: En réponse à votre commentaire sur IMockedComplexObject, je cite le commentaire de Cwash sur une autre réponse: "Vous ne vous moquez pas de ce que vous essayez de tester, vous vous moquez de ce que vous n'essayez pas de tester."
Brian
39

Un test erroné est peu susceptible de casser votre code de production. Du moins, pas pire que de ne pas avoir de test du tout. Ce n'est donc pas un "point d'échec": les tests n'ont pas besoin d'être corrects pour que le produit fonctionne réellement. Ils doivent peut-être être corrects avant d'être validés comme fonctionnant, mais le processus de correction des tests interrompus ne met pas en danger votre code d'implémentation.

Vous pouvez considérer les tests, même les tests triviaux comme ceux-ci, comme une seconde opinion sur ce que le code est censé faire. Une opinion est le test, l'autre est la mise en œuvre. S'ils ne sont pas d'accord, alors vous savez que vous avez un problème et vous regardez de plus près.

C'est également utile si quelqu'un souhaite à l'avenir implémenter la même interface à partir de zéro. Ils ne devraient pas avoir à lire la première implémentation pour savoir ce que signifie Discount, et les tests agissent comme une sauvegarde sans ambiguïté de toute description écrite de l'interface que vous pourriez avoir.

Cela dit, vous échangez du temps. S'il y a d'autres tests que vous pourriez écrire en utilisant le temps que vous gagnez en sautant ces tests triviaux, ils seraient peut-être plus précieux. Cela dépend vraiment de la configuration de votre test et de la nature de l'application. Si la remise est importante pour l'application, vous allez quand même attraper des bogues dans cette méthode lors des tests fonctionnels. Tous les tests unitaires n'est que vous laissez les attraper au point que vous testez cette unité, lorsque l'emplacement de l'erreur sera immédiatement évident, au lieu d'attendre jusqu'à ce que l'application est intégrée ensemble et l'emplacement de l'erreur peut être moins évidente.

Au fait, personnellement, je n'utiliserais pas 100 comme prix dans le cas de test (ou plutôt, si je le faisais, j'ajouterais un autre test avec un autre prix). La raison en est que quelqu'un à l'avenir pourrait penser que la remise est censée être un pourcentage. L'un des objectifs de tests triviaux comme celui-ci est de s'assurer que les erreurs de lecture de la spécification sont corrigées.

[Concernant l'édition: je pense qu'il est inévitable qu'une spécification incorrecte soit un point d'échec. Si vous ne savez pas ce que l'application est censée faire, il y a de fortes chances qu'elle ne le fasse pas. Mais écrire des tests pour refléter les spécifications n'amplifie pas ce problème, cela ne parvient simplement pas à le résoudre. Vous n'ajoutez donc pas de nouveaux points de défaillance, vous représentez simplement les défauts existants dans le code au lieu de la documentation de gaufres .]

Steve Jessop
la source
4
Un mauvais test laissera du code cassé dans la nature. C'est là que l'échec est introduit. Cela donne un faux sentiment de confiance.
FlySwat
9
C'est vrai, mais ne pas avoir de test permet également de faire sortir du code cassé. L'erreur est de penser que si le code passe les tests unitaires, il doit être correct - j'ai été guéri de cela assez tôt dans ma carrière. Ainsi, un test unitaire cassé ne laisse pas le code cassé dans la nature, il le laisse seulement sortir dans les tests d'intégration.
Steve Jessop
7
De plus, même un test erroné peut détecter du code cassé, à condition qu'il contienne des erreurs différentes de l'implémentation. C'est ce que je veux dire, les tests ne doivent absolument pas être corrects, ils sont là pour attirer votre attention sur des sujets de préoccupation.
Steve Jessop
2
réponse très intéressante.
Peter
22

Tout ce que je vois, ce sont plus de points d'échec possibles, sans réel retour bénéfique, si le prix réduit est erroné, l'équipe de test trouvera toujours le problème, comment les tests unitaires ont-ils sauvé du travail?

Les tests unitaires ne sont pas vraiment censés sauver du travail, ils sont censés vous aider à trouver et à prévenir les bogues. C'est plus de travail, mais c'est le bon type de travail. Il s'agit de réfléchir à votre code aux plus bas niveaux de granularité et d'écrire des cas de test qui prouvent qu'il fonctionne dans les conditions attendues , pour un ensemble donné d'entrées. Il isole les variables pour que vous puissiez gagner du temps en cherchant au bon endroit quand un bogue se présente. C'est enregistrer cette suite de tests afin que vous puissiez les utiliser encore et encore lorsque vous devez faire un changement plus tard.

Je pense personnellement que la plupart des méthodologies ne sont pas beaucoup d'étapes retirées de l' ingénierie logicielle culte du fret , TDD inclus, mais vous n'avez pas à adhérer à un TDD strict pour profiter des avantages des tests unitaires. Gardez les bonnes pièces et jetez celles qui ne rapportent que peu d'avantages.

Enfin, la réponse à votre question titulaire " Comment testez-vous un test unitaire? " Est que vous ne devriez pas avoir à le faire. Chaque test unitaire doit être simple et insensé. Appelez une méthode avec une entrée spécifique et comparez-la à sa sortie attendue. Si la spécification d'une méthode change, vous pouvez vous attendre à ce que certains des tests unitaires de cette méthode doivent également changer. C'est l'une des raisons pour lesquelles vous effectuez des tests unitaires à un niveau de granularité si bas, de sorte que seuls certains des tests unitaires doivent changer. Si vous constatez que les tests pour de nombreuses méthodes différentes changent pour un changement dans une exigence, il se peut que vous ne testiez pas à un niveau de granularité suffisamment fin.

Bill le lézard
la source
"Appelez une méthode avec une entrée spécifique et comparez-la à sa sortie attendue." mais que faire si la sortie est de type complexe ... comme un document XML. Vous ne pouvez pas simplement "==", vous devrez écrire un code spécifique de comparaison, et peut-être que votre méthode de comparaison pourrait être boguée?
andy
@andy: Vous devez tester votre méthode de comparaison séparément. Une fois que vous l'avez testé en profondeur, vous pouvez vous fier à ce qu'il fonctionne dans d'autres tests.
Bill the Lizard
cool, merci Bill. J'ai commencé à travailler dans un nouvel endroit et c'est ma première fois avec les tests unitaires. Je pense qu'en principe cela fonctionne, et nous utilisons Cruise Control là où c'est vraiment utile, mais de grands ensembles de tests semblent subir le même sort que le code hérité ... Je n'en suis tout simplement pas sûr ....
andy
11

Les tests unitaires sont là pour que vos unités (méthodes) fassent ce que vous attendez. L'écriture du test vous oblige d'abord à réfléchir à ce que vous attendez avant d'écrire le code. Penser avant de faire est toujours une bonne idée.

Les tests unitaires doivent refléter les règles métier. Certes, il peut y avoir des erreurs dans le code, mais l'écriture du test en premier vous permet de l'écrire du point de vue de la règle métier avant qu'un code n'ait été écrit. Écrire le test après, je pense, est plus susceptible de conduire à l'erreur que vous décrivez parce que vous savez comment le code l'implémente et que vous êtes simplement tenté de vous assurer que l'implémentation est correcte - pas que l'intention est correcte.

De plus, les tests unitaires ne sont qu'une forme - et la plus basse, en plus - des tests que vous devriez écrire. Des tests d'intégration et des tests d'acceptation doivent également être rédigés, ces derniers par le client, si possible, pour s'assurer que le système fonctionne comme prévu. Si vous trouvez des erreurs lors de ce test, revenez en arrière et écrivez des tests unitaires (qui échouent) pour tester le changement de fonctionnalité pour le faire fonctionner correctement, puis modifiez votre code pour que le test réussisse. Vous avez maintenant des tests de régression qui capturent vos corrections de bogues.

[ÉDITER]

Une autre chose que j'ai trouvée avec le TDD. Cela force presque une bonne conception par défaut. Cela est dû au fait que les conceptions hautement couplées sont presque impossibles à tester isolément. Il ne faut pas beaucoup de temps avec TDD pour comprendre que l'utilisation d'interfaces, d'inversion de contrôle et d'injection de dépendances - tous les modèles qui amélioreront votre conception et réduiront le couplage - sont vraiment importantes pour le code testable.

Tvanfosson
la source
C'est peut-être là que réside mon problème. Je peux visualiser l'algorithme d'une règle métier plus facilement que je ne peux visualiser le résultat, donc je n'ai aucun problème à implémenter le code lui-même, mais je considère que la simulation de la règle est redondante. Peut-être que c'est comme ça que je pense.
FlySwat
C'est exactement ce que vous faites dans un test unitaire. Décomposez cet algorithme en morceaux et vérifiez chaque pièce. En général, je trouve que mon code s'écrit lui-même car j'ai déjà écrit l'attente dans mon test unitaire.
tvanfosson
Mock est un terme surchargé dans l'espace de test. Vous ne vous moquez pas de ce que vous essayez de tester, vous vous moquez de ce que vous n'essayez pas de tester ... Lorsque vous écrivez le test de votre règle métier, vous créez un code qui l'invoque - il ne se moque pas du tout .
cwash le
@cwash - Je ne sais pas comment votre commentaire s'applique à ma réponse. Je n'ai pas parlé de moquerie ... et je suis d'accord avec votre observation.
tvanfosson
@tvanfosson - mon dernier commentaire était en réponse à @FlySwat "... se moquant de la règle comme redondante." Désolé j'ai oublié de préciser.
cwash
10

Comment tester un test ? Le test de mutation est une technique précieuse que j'ai personnellement utilisée avec un effet étonnamment bon. Lisez l'article lié pour plus de détails et des liens vers encore plus de références académiques, mais en général, il "teste vos tests" en modifiant votre code source (en remplaçant "x + = 1" par "x - = 1" par exemple), puis réexécutez vos tests, en vous assurant qu'au moins un test échoue. Toutes les mutations qui n'entraînent pas d'échecs de test sont signalées pour une enquête ultérieure.

Vous seriez surpris de voir comment vous pouvez avoir une couverture à 100% des lignes et des succursales avec un ensemble de tests qui semblent complets, et pourtant vous pouvez fondamentalement changer ou même commenter une ligne dans votre source sans qu'aucun des tests ne se plaint. Souvent, cela revient à ne pas tester avec les bonnes entrées pour couvrir tous les cas limites, parfois c'est plus subtil, mais dans tous les cas, j'ai été impressionné par tout ce qui en a résulté.

Andrzej Doyle
la source
1
+1 concept intéressant dont je n'avais pas encore entendu parler
Wim Coenen
9

Lors de l'application du développement piloté par les tests (TDD), on commence par un test qui échoue . Cette étape, qui peut sembler inutile, est en fait là pour vérifier que le test unitaire teste quelque chose. En effet, si le test n'échoue jamais, il n'apporte aucune valeur et pire, conduit à une fausse confiance car vous vous fiez à un résultat positif qui ne prouve rien.

En suivant strictement ce processus, toutes les «unités» sont protégées par le filet de sécurité des tests unitaires, même les plus banals.

Assert.IsEqual(p.DiscountPrice,90);

Il n'y a aucune raison pour que le test évolue dans cette direction - ou je manque quelque chose dans votre raisonnement. Lorsque le prix est de 100 et la remise de 20, le prix de remise est de 80. C'est comme un invariant.

Imaginez maintenant que votre logiciel doit prendre en charge un autre type de remise en fonction du pourcentage, peut-être en fonction du volume acheté, votre méthode Product :: DiscountPrice () peut devenir plus compliquée. Et il est possible que l'introduction de ces changements enfreigne la règle de remise simple que nous avions initialement. Ensuite, vous verrez la valeur de ce test qui détectera immédiatement la régression.


Rouge - Vert - Refactoriser - c'est pour se souvenir de l'essence du processus TDD.

Le rouge fait référence à la barre rouge de JUnit lorsqu'un test échoue.

Le vert est la couleur de la barre de progression JUnit lorsque tous les tests réussissent.

Refactoriser sous condition verte: supprimez toute duplication, améliorez la lisibilité.


Maintenant, pour aborder votre point sur les «3-4 couches au-dessus du code», cela est vrai dans un processus traditionnel (en cascade), pas lorsque le processus de développement est agile. Et l'agilité est le monde d'où vient le TDD; TDD est la pierre angulaire de la programmation eXtreme .

Agile concerne la communication directe plutôt que les documents d'exigences jetés par-dessus le mur.

philant
la source
8

Alors que je suis tout à fait pour les tests unitaires, je me demande parfois si cette forme de premier développement de test est vraiment bénéfique ...

De petits tests triviaux comme celui-ci peuvent être le "canari dans la mine de charbon" pour votre base de code, alertant du danger avant qu'il ne soit trop tard. Les tests triviaux sont utiles à garder car ils vous aident à obtenir les bonnes interactions.

Par exemple, pensez à un test trivial mis en place pour savoir comment utiliser une API que vous ne connaissez pas. Si ce test est pertinent par rapport à ce que vous faites dans le code qui utilise l'API "pour de vrai", il est utile de garder ce test à portée de main. Lorsque l'API publie une nouvelle version et que vous devez mettre à niveau. Vous avez maintenant vos hypothèses sur la façon dont vous vous attendez à ce que l'API se comporte enregistrées dans un format exécutable que vous pouvez utiliser pour intercepter les régressions.

... [I] n processus réel, vous avez 3-4 couches au-dessus de votre code (demande commerciale, document d'exigences, document d'architecture), où la règle métier réellement définie (le prix de remise est le prix - la remise) pourrait être mal définie. Si tel est le cas, votre test unitaire ne vous dit rien.

Si vous codez depuis des années sans écrire de tests, il se peut que vous ne sachiez pas immédiatement qu'il y a une valeur. Mais si vous pensez que la meilleure façon de travailler est de «publier tôt, publier souvent» ou «agile» en ce sens que vous voulez pouvoir déployer rapidement / continuellement, alors votre test signifie définitivement quelque chose. La seule façon de le faire est de légitimer chaque modification que vous apportez au code avec un test. Quelle que soit la taille du test, une fois que vous avez une suite de tests verte, vous pouvez théoriquement déployer. Voir aussi «production continue» et «bêta perpétuelle».

Vous n'avez pas non plus besoin d'être "test premier" pour avoir cet état d'esprit, mais c'est généralement le moyen le plus efficace d'y arriver. Lorsque vous faites du TDD, vous vous enfermez dans un petit cycle Red Green Refactor de deux à trois minutes. À aucun moment, vous ne pouvez vous arrêter et partir et avoir un désordre complet sur vos mains qui prendra une heure à déboguer et à remonter.

De plus, votre test unitaire est un autre point d'échec ...

Un test réussi est celui qui démontre une défaillance du système. Un test échoué vous alertera d'une erreur dans la logique du test ou dans la logique de votre système. Le but de vos tests est de casser votre code ou de prouver qu'un scénario fonctionne.

Si vous écrivez des tests après le code, vous courez le risque d'écrire un test qui est «mauvais» car pour voir que votre test fonctionne vraiment, vous devez le voir à la fois cassé et fonctionnel. Lorsque vous écrivez des tests après le code, cela signifie que vous devez "lancer le piège" et introduire un bogue dans le code pour voir le test échouer. La plupart des développeurs ne sont pas seulement inquiets à ce sujet, mais soutiennent que c'est une perte de temps.

Que gagnons-nous ici?

Il y a certainement un avantage à faire les choses de cette façon. Michael Feathers définit le «code hérité» comme un «code non testé». Lorsque vous adoptez cette approche, vous légitimez chaque modification que vous apportez à votre base de code. C'est plus rigoureux que de ne pas utiliser de tests, mais lorsqu'il s'agit de maintenir une base de code volumineuse, cela se paie de lui-même.

En parlant de plumes, il y a deux excellentes ressources que vous devriez consulter à ce sujet:

Ces deux explications expliquent comment intégrer ces types de pratiques et de disciplines dans des projets qui ne sont pas «Greenfield». Ils fournissent des techniques pour écrire des tests autour de composants étroitement couplés, de dépendances câblées et de choses sur lesquelles vous n'avez pas nécessairement de contrôle. Il s'agit de trouver des «coutures» et de tester autour de celles-ci.

[S] i le prix réduit est erroné, l'équipe de test trouvera toujours le problème, comment les tests unitaires ont-ils sauvé du travail?

De telles habitudes sont comme un investissement. Les retours ne sont pas immédiats; ils s'accumulent avec le temps. L'alternative à ne pas tester est essentiellement de s'endetter de ne pas pouvoir détecter les régressions, d'introduire du code sans crainte d'erreurs d'intégration ou de prendre des décisions de conception. La beauté est que vous légitimisez chaque changement introduit dans votre base de code.

Qu'est-ce que j'oublie ici? S'il vous plaît, apprenez-moi à aimer TDD, car j'ai du mal à l'accepter comme utile jusqu'à présent. Je veux aussi, parce que je veux rester progressiste, mais cela n'a pas de sens pour moi.

Je considère cela comme une responsabilité professionnelle. C'est un idéal vers lequel tendre. Mais c'est très difficile à suivre et fastidieux. Si vous vous en souciez et pensez que vous ne devriez pas produire de code qui ne soit pas testé, vous pourrez trouver la volonté d'apprendre de bonnes habitudes de test. Une chose que je fais beaucoup maintenant (comme d'autres) est de me donner une heure pour écrire du code sans aucun test du tout, puis d'avoir la discipline de le jeter. Cela peut sembler inutile, mais ce n'est pas vraiment le cas. Ce n'est pas comme si cet exercice coûtait des matériaux physiques à une entreprise. Cela m'a aidé à comprendre le problème et à écrire du code de manière à ce qu'il soit à la fois de meilleure qualité et testable.

Mon conseil serait finalement que si vous n'avez vraiment pas le désir d'être bon dans ce domaine, alors ne le faites pas du tout. De mauvais tests qui ne sont pas maintenus, qui ne fonctionnent pas bien, etc. peuvent être pires que de ne pas avoir de tests. Il est difficile d'apprendre par vous-même et vous n'aimerez probablement pas cela, mais il sera presque impossible d'apprendre si vous n'avez pas le désir de le faire ou si vous ne pouvez pas y voir suffisamment de valeur pour justifie l'investissement en temps.

Quelques personnes n'arrêtent pas de dire que les tests aident à appliquer les spécifications. D'après mon expérience, les spécifications se sont également trompées, le plus souvent ...

Le clavier d'un développeur est l'endroit où le caoutchouc rencontre la route. Si la spécification est fausse et que vous ne lève pas le drapeau dessus, il est fort probable que vous en soyez blâmé. Ou du moins votre code le fera. La discipline et la rigueur impliquées dans les tests sont difficiles à respecter. Ce n'est pas du tout facile. Cela demande de la pratique, beaucoup d'apprentissage et beaucoup d'erreurs. Mais finalement, cela porte ses fruits. Sur un projet au rythme rapide et en évolution rapide, c'est la seule façon de dormir la nuit, peu importe si cela vous ralentit.

Une autre chose à laquelle il faut penser ici est que des techniques qui sont fondamentalement les mêmes que les tests ont fait leurs preuves dans le passé: "salle blanche" et "conception par contrat" ​​ont toutes deux tendance à produire les mêmes types de constructions de "méta" -code que les tests le font et les appliquent à différents moments. Aucune de ces techniques n'est une solution miracle, et la rigueur va vous coûter en fin de compte la portée des fonctionnalités que vous pouvez offrir en termes de délai de mise sur le marché. Mais ce n'est pas de cela qu'il s'agit. Il s'agit de pouvoir maintenir ce que vous livrez. Et c'est très important pour la plupart des projets.

cwash
la source
7

Les tests unitaires fonctionnent de manière très similaire à la comptabilité en partie double. Vous énoncez la même chose (règle métier) de deux manières très différentes (en tant que règles programmées dans votre code de production et en tant qu'exemples simples et représentatifs dans vos tests). Il est très peu probable que vous fassiez la même erreur dans les deux, donc s'ils sont tous les deux d'accord, il est peu probable que vous vous trompiez.

Comment les tests vont-ils en valoir la peine? D'après mon expérience, au moins quatre façons, au moins lors du développement piloté par les tests:

  • cela vous aide à créer une conception bien découplée. Vous ne pouvez tester que le code de test unitaire bien découplé;
  • cela vous aide à déterminer quand vous avez terminé. Le fait de devoir spécifier le comportement nécessaire dans les tests permet de ne pas créer de fonctionnalités dont vous n'avez pas réellement besoin et de déterminer quand la fonctionnalité est terminée;
  • cela vous donne un filet de sécurité pour les refactorisations, ce qui rend le code beaucoup plus propice aux changements; et
  • cela vous fait gagner beaucoup de temps de débogage, ce qui est terriblement coûteux (j'ai entendu des estimations selon lesquelles traditionnellement, les développeurs passent jusqu'à 80% de leur temps à déboguer).
Ilja Preuß
la source
5

La plupart des tests unitaires, des hypothèses de test. Dans ce cas, le prix réduit doit être le prix moins la réduction. Si vos hypothèses sont fausses, je parie que votre code est également faux. Et si vous faites une erreur stupide, le test échouera et vous le corrigerez.

Si les règles changent, le test échouera et c'est une bonne chose. Vous devez donc également modifier le test dans ce cas.

En règle générale, si un test échoue tout de suite (et que vous n'utilisez pas le test first design), soit le test, soit le code est erroné (ou les deux si vous passez une mauvaise journée). Vous utilisez le bon sens (et éventuellement par les spécifications) pour corriger le code incriminé et relancer le test.

Comme Jason l'a dit, les tests sont la sécurité. Et oui, parfois ils introduisent du travail supplémentaire en raison de tests défectueux. Mais la plupart du temps, ils font gagner du temps. (Et vous avez l'occasion parfaite de punir le mec qui brise le test (on parle de poulet en caoutchouc)).

Toon Krijthe
la source
4

Testez tout ce que vous pouvez. Même des erreurs insignifiantes, comme oublier de convertir des mètres en pieds peuvent avoir des effets secondaires très coûteux. Écrivez un test, écrivez le code pour le vérifier, faites-le passer, passez à autre chose. Qui sait à un moment donné dans le futur, quelqu'un peut changer le code de réduction. Un test peut détecter le problème.

Jim C
la source
Cela ne répond à aucune de mes pensées. Je comprends le mantra de base du TDD ... Je ne vois pas l'avantage.
FlySwat
4

Je vois les tests unitaires et le code de production comme ayant une relation symbiotique. En termes simples: l'un teste l'autre. Et les deux testent le développeur.

Johnsyweb
la source
3

N'oubliez pas que le coût de la correction des défauts augmente (de façon exponentielle) à mesure que les défauts vivent tout au long du cycle de développement. Oui, l'équipe de test peut détecter le défaut, mais il faudra (généralement) plus de travail pour isoler et corriger le défaut à partir de ce point que si un test unitaire avait échoué, et il sera plus facile d'introduire d'autres défauts tout en le corrigeant si vous n'ont pas de tests unitaires à exécuter.

C'est généralement plus facile à voir avec quelque chose de plus qu'un exemple trivial ... et avec des exemples triviaux, eh bien, si vous gâchez le test unitaire d'une manière ou d'une autre, la personne qui l'examinera détectera l'erreur dans le test ou l'erreur dans le code, ou tous les deux. (Ils sont en cours de révision, n'est-ce pas?) Comme le souligne tvanfosson , les tests unitaires ne sont qu'une partie d'un plan SQA.

En un sens, les tests unitaires sont une assurance. Ils ne garantissent pas que vous détecterez tous les défauts, et il peut parfois sembler que vous dépensez beaucoup de ressources, mais lorsqu'ils détectent des défauts que vous pouvez réparer, vous dépenserez beaucoup moins. que si vous n'aviez aucun test et que vous deviez corriger tous les défauts en aval.

Dave DuPlantis
la source
3

Je comprends votre point de vue, mais il est clairement exagéré.

Votre argument est fondamentalement: les tests introduisent un échec. Par conséquent, les tests sont mauvais / perte de temps.

Bien que cela puisse être vrai dans certains cas, ce n'est guère la majorité.

TDD suppose: Plus de tests = Moins d'échecs.

Les tests sont plus susceptibles de détecter les points de défaillance que de les introduire.

Rayon
la source
1

Encore plus d'automatisation peut aider ici! Oui, l'écriture de tests unitaires peut demander beaucoup de travail, alors utilisez des outils pour vous aider. Jetez un œil à quelque chose comme Pex, de Microsoft, si vous utilisez .Net. Il créera automatiquement des suites de tests unitaires pour vous en examinant votre code. Il proposera des tests offrant une bonne couverture, en essayant de couvrir tous les chemins à travers votre code.

Bien sûr, rien qu'en regardant votre code, il ne peut pas savoir ce que vous essayiez réellement de faire, donc il ne sait pas si c'est correct ou non. Mais, cela générera des cas de tests intéressants pour vous, et vous pourrez ensuite les examiner et voir s'il se comporte comme prévu.

Si vous allez ensuite plus loin et écrivez des tests unitaires paramétrés (vous pouvez vraiment les considérer comme des contrats), cela générera des cas de tests spécifiques à partir de ceux-ci, et cette fois, il pourra savoir si quelque chose ne va pas, car vos assertions dans vos tests échoueront.


la source
1

J'ai réfléchi un peu à une bonne manière de répondre à cette question et je voudrais faire un parallèle avec la méthode scientifique. OMI, vous pourriez reformuler cette question, "Comment expérimentez-vous une expérience?"

Les expériences vérifient les hypothèses empiriques (hypothèses) sur l'univers physique. Les tests unitaires testeront les hypothèses sur l'état ou le comportement du code qu'ils appellent. Nous pouvons parler de la validité d'une expérience, mais c'est parce que nous savons, à travers de nombreuses autres expériences, que quelque chose ne va pas. Il n'a pas à la fois de validité convergente et de preuves empiriques . Nous ne concevons pas une nouvelle expérience pour tester ou vérifier la validité d' une expérience , mais nous pouvons concevoir une expérience complètement nouvelle .

Donc, comme les expériences , nous ne décrivons pas la validité d'un test unitaire en fonction de sa réussite ou non à un test unitaire lui-même. Avec d'autres tests unitaires, il décrit les hypothèses que nous formulons sur le système qu'il teste. De plus, comme pour les expériences, nous essayons d'éliminer autant de complexité que possible de ce que nous testons. "Aussi simple que possible, mais pas plus simple."

Contrairement aux expériences , nous avons une astuce dans notre manche pour vérifier que nos tests sont valides autres que la validité convergente. Nous pouvons intelligemment introduire un bogue dont nous savons qu'il devrait être détecté par le test, et voir si le test échoue effectivement. (Si seulement nous pouvions faire cela dans le monde réel, nous dépendrions beaucoup moins de cette chose de validité convergente!) Un moyen plus efficace de le faire est de regarder votre test échouer avant de l'implémenter (l'étape rouge en rouge, vert, refactor ).

cwash
la source
1

Vous devez utiliser le paradigme correct lors de l'écriture des tests.

  1. Commencez par rédiger vos tests.
  2. Assurez-vous qu'ils ne commencent pas par.
  3. Faites-les réussir.
  4. Examen du code avant de vérifier votre code (assurez-vous que les tests sont examinés.)

Vous ne pouvez pas toujours être sûr, mais ils améliorent les tests globaux.

Jonathan
la source
0

Même si vous ne testez pas votre code, il sera sûrement testé en production par vos utilisateurs. Les utilisateurs sont très créatifs pour essayer de planter votre logiciel et trouver des erreurs même non critiques.

La correction des bogues en production est beaucoup plus coûteuse que la résolution des problèmes en phase de développement. En conséquence, vous perdrez des revenus en raison d'un exode de clients. Vous pouvez compter sur 11 clients perdus ou non gagnés pour 1 client en colère.


la source