Je crois que c'est une idée fausse de toute façon à laquelle je peux penser.
Le code de test qui teste le code de production n'est pas du tout similaire. Je vais démontrer en python:
def multiply(a, b):
"""Multiply ``a`` by ``b``"""
return a*b
Un test simple serait alors:
def test_multiply():
assert multiply(4, 5) == 20
Les deux fonctions ont une définition similaire mais les deux font des choses très différentes. Pas de code en double ici. ;-)
Il arrive également que des personnes écrivent des tests en double ayant essentiellement une assertion par fonction de test. C'est de la folie et j'ai vu des gens faire ça. C'est une mauvaise pratique.
def test_multiply_1_and_3():
"""Assert that a multiplication of 1 and 3 is 3."""
assert multiply(1, 3) == 3
def test_multiply_1_and_7():
"""Assert that a multiplication of 1 and 7 is 7."""
assert multiply(1, 7) == 7
def test_multiply_3_and_4():
"""Assert that a multiplication of 3 and 4 is 12."""
assert multiply(3, 4) == 12
Imaginez faire cela pour plus de 1000 lignes de code efficaces. Au lieu de cela, vous testez par «fonctionnalité»:
def test_multiply_positive():
"""Assert that positive numbers can be multiplied."""
assert multiply(1, 3) == 3
assert multiply(1, 7) == 7
assert multiply(3, 4) == 12
def test_multiply_negative():
"""Assert that negative numbers can be multiplied."""
assert multiply(1, -3) == -3
assert multiply(-1, -7) == 7
assert multiply(-3, 4) == -12
Maintenant, lorsque des fonctionnalités sont ajoutées / supprimées, je n'ai plus qu'à envisager d'ajouter / supprimer une fonction de test.
Vous avez peut-être remarqué que je n'ai pas appliqué de for
boucles. C'est parce que répéter certaines choses est bon. Quand j'aurais appliqué des boucles, le code serait beaucoup plus court. Mais lorsqu'une assertion échoue, elle peut masquer la sortie affichant un message ambigu. Si cela se produit, vos tests seront moins utiles et vous aurez besoin d'un débogueur pour inspecter les problèmes.
assert multiply(1,3)
échouerait mais vous n'obtiendrez pas non plus le rapport de test ayant échouéassert multiply(3,4)
.def test_shuffle
effectue deux assertions.assert multiply(*, *) == *
vous pouvez donc définir uneassert_multiply
fonction. Dans le scénario actuel, peu importe le nombre de lignes et la lisibilité, mais par des tests plus longs, vous pouvez réutiliser des assertions compliquées, des montages, du code générant des montages, etc. Je ne sais pas si c'est une meilleure pratique, mais je le fais habituellement ce.Non, ce n'est pas vrai.
Les tests ont un objectif différent de celui de votre implémentation:
la source
Non. DRY consiste à écrire du code une seule fois pour effectuer une tâche particulière, les tests valident que la tâche est effectuée correctement. Cela ressemble un peu à un algorithme de vote, où, évidemment, utiliser le même code serait inutile.
la source
Non, l'objectif ultime de DRY signifierait en fait l'élimination de tout le code de production .
Si nos tests pouvaient être des spécifications parfaites de ce que nous voulons que le système fasse, nous n'aurions qu'à générer automatiquement le code de production (ou les binaires) correspondant, supprimant ainsi efficacement la base du code de production en soi.
C'est en fait ce que des approches telles que l'architecture pilotée par les modèles prétendent atteindre - une source de vérité unique conçue par l'homme, dont tout est dérivé par le calcul.
Je ne pense pas que l'inverse (se débarrasser de tous les tests) soit souhaitable car:
la source
Parce que parfois, se répéter c'est bien. Aucun de ces principes n'est censé être appliqué en toutes circonstances sans question ni contexte. J'ai parfois écrit des tests contre une version naïve (et lente) d'un algorithme, ce qui est une violation assez nette de DRY, mais certainement bénéfique.
la source
Étant donné que les tests unitaires consistent à rendre les changements involontaires plus difficiles, ils peuvent parfois aussi rendre les changements intentionnels plus difficiles. Ce fait est en effet lié au principe DRY.
Par exemple, si vous avez une fonction
MyFunction
qui est appelée dans le code de production à un seul endroit et que vous écrivez 20 tests unitaires pour elle, vous pouvez facilement finir par avoir 21 places dans votre code où cette fonction est appelée. Maintenant, lorsque vous devez changer la signature deMyFunction
, ou la sémantique, ou les deux (car certaines exigences changent), vous avez 21 emplacements à modifier au lieu d'un seul. Et la raison est en effet une violation du principe DRY: vous avez répété (au moins) le même appel de fonction àMyFunction
21 fois.L'approche correcte pour un tel cas consiste également à appliquer le principe DRY à votre code de test: lors de l'écriture de 20 tests unitaires, encapsulez les appels à
MyFunction
dans vos tests unitaires en quelques fonctions d'assistance (idéalement une seule), qui sont utilisées par le 20 tests unitaires. Idéalement, vous vous retrouvez avec seulement deux endroits dans votre appel de codeMyFunction
: un de votre code de production et un de vos tests unitaires. Ainsi, lorsque vous devrez modifier la signature deMyFunction
plus tard, vous n'aurez que quelques emplacements à modifier dans vos tests."Quelques endroits" sont toujours plus qu'un "endroit" (ce que vous obtenez sans aucun test unitaire), mais les avantages d'avoir des tests unitaires devraient largement l'emporter sur l'avantage d'avoir moins de code à changer (sinon vous faites des tests unitaires complètement faux).
la source
L'un des plus grands défis à la création de logiciels est de capturer les exigences; c'est pour répondre à la question "que doit faire ce logiciel?" Les logiciels ont besoin d'exigences exactes pour définir avec précision ce que le système doit faire, mais ceux qui définissent les besoins en systèmes logiciels et en projets incluent souvent des personnes qui n'ont pas de connaissances en logiciel ou formelles (mathématiques). Le manque de rigueur dans la définition des exigences a forcé le développement de logiciels à trouver un moyen de valider les logiciels selon les exigences.
L'équipe de développement s'est retrouvée à traduire la description familière d'un projet en exigences plus rigoureuses. La discipline des tests a fusionné en tant que point de contrôle pour le développement de logiciels, pour combler l'écart entre ce qu'un client dit vouloir et ce que le logiciel comprend. Tant les développeurs de logiciels que l'équipe de qualité / tests forment une compréhension de la spécification (informelle), et chacun (indépendamment) écrit des logiciels ou des tests pour s'assurer que leur compréhension est conforme. L'ajout d'une autre personne pour comprendre les exigences (imprécises) a ajouté des questions et une perspective différente pour affiner davantage la précision des exigences.
Comme il y a toujours eu des tests d'acceptation, il était naturel d'élargir le rôle de test pour écrire des tests automatisés et unitaires. Le problème était que cela signifiait embaucher des programmeurs pour faire des tests, et donc vous avez restreint la perspective de l'assurance qualité aux programmeurs faisant des tests.
Cela dit, vous faites probablement de mauvais tests si vos tests diffèrent peu des programmes réels. La suggestion de Msdy serait de se concentrer davantage sur quoi dans les tests, et moins sur comment.
L'ironie est que plutôt que de capturer une spécification formelle des exigences à partir de la description familière, l'industrie a choisi de mettre en œuvre des tests ponctuels comme code pour automatiser les tests. Plutôt que de produire des exigences formelles auxquelles un logiciel pourrait être conçu pour répondre, l'approche adoptée a consisté à tester quelques points, plutôt que de concevoir un logiciel en utilisant une logique formelle. Il s'agit d'un compromis, mais il a été assez efficace et relativement réussi.
la source
Si vous pensez que votre code de test est trop similaire à votre code d'implémentation, cela peut être une indication que vous utilisez trop un framework de simulation. Les tests factices à un niveau trop bas peuvent aboutir à une configuration de test ressemblant beaucoup à la méthode testée. Essayez d'écrire des tests de niveau supérieur qui sont moins susceptibles de se casser si vous modifiez votre implémentation (je sais que cela peut être difficile, mais si vous pouvez le gérer, vous aurez une suite de tests plus utile en conséquence).
la source
Les tests unitaires ne doivent pas inclure une duplication du code testé, comme cela a déjà été noté.
J'ajouterais cependant que les tests unitaires ne sont généralement pas aussi SECS que le code de "production", car la configuration a tendance à être similaire (mais pas identique) entre les tests ... surtout si vous avez un nombre important de dépendances dont vous vous moquez / truquer.
Il est bien sûr possible de refactoriser ce genre de chose dans une méthode de configuration commune (ou un ensemble de méthodes de configuration) ... mais j'ai trouvé que ces méthodes de configuration ont tendance à avoir de longues listes de paramètres et à être plutôt cassantes.
Soyez donc pragmatique. Si vous pouvez consolider le code de configuration sans compromettre la maintenabilité, faites-le par tous les moyens. Mais si l'alternative est un ensemble complexe et fragile de méthodes de configuration, un peu de répétition dans vos méthodes de test est OK.
Un évangéliste local TDD / BDD le dit ainsi:
"Votre code de production doit être SEC. Mais il est OK que vos tests soient" humides "."
la source
Ce n'est pas vrai, les tests décrivent les cas d'utilisation, tandis que le code décrit un algorithme qui passe les cas d'utilisation, ce qui est plus général. Par TDD, vous commencez par écrire des cas d'utilisation (probablement basés sur la user story) et ensuite vous implémentez le code nécessaire pour passer ces cas d'utilisation. Vous écrivez donc un petit test, un petit morceau de code, et ensuite vous refactorisez si nécessaire pour vous débarrasser des répétitions. Voilà comment ça fonctionne.
Par tests, il peut aussi y avoir des répétitions. Par exemple, vous pouvez réutiliser des appareils, du code générant des appareils, des assertions compliquées, etc. , lorsque vous recherchez le bogue dans le code pendant une demi-heure et que le test est incorrect ... xD
la source