Comment ne pas tester, partie 1

By | août 9, 2019

Expert Python

ou Test de couverture complet
ou Plus c'est mieux, mieux tester

La mise en place

Dans l’intérêt de cet article, disons que j’ai un paquet Python qui a besoin d’être testé.

  • Il est écrit complètement en Python.
  • Il a une spécification décrivant complètement l'API
  • La spécification est si complète qu'elle couvre également les comportements et les effets secondaires
  • L'API est la seule interface exposée par ce programme
  • Il a été écrit par nous
  • Il a été écrit récemment
  • Il utilise uniquement la base Python

Donc:

  • pas d'interface graphique
  • pas d'autre API que celle bien définie et spécifiée
  • nous avons un accès complet à la source
  • nous nous souvenons de toutes les décisions pour tout le code
  • pas de modules tiers qui pourraient être floconneux

C'est le programme idéal pour écrire et tester.
Vous pouvez dire que c'est inventé, parce que je n'ai jamais vu tout ce qui précède être vrai.
En réalité, beaucoup de ces objets ne seront pas présents. Mais allons-y pour le moment et voyons où cela nous mènera.

Stratégie de test: tout tester

Je veux que ce paquet soit solide comme un roc. Je ne souhaite pas que les clients signalent des anomalies.
Alors pourquoi ne pas simplement aller de l'avant et planifier de tout tester.

Fonctionnalité:

  • Chaque fonction ou méthode de l'API doit avoir au moins un test écrit pour vérifier son comportement correct et au moins un test de chacune des conditions d'erreur possibles si nous transmettons une entrée erronée à la fonction ou à la méthode.
  • Chaque fonctionnalité ou comportement de la spécification doit comporter au moins un test qui vérifie que le package répond à cette fonctionnalité ou à ce comportement.

Indépendance des composants:

  • Chaque package / fonction / classe / méthode / module doit être testé séparément, les dépendances doivent être simulées.

L'intégration:

  • Chaque package / fonction / classe / méthode / module doit également être testé avec toutes ses dépendances NON simulé.

Le plan consiste simplement à tester les filtre à bière du paquet.
Certains tests sont bons.
Des tests complets et complets doivent être meilleurs. Droite? Faux.

Pourquoi c'est mauvais

Dans mon esprit, il y a pas mal de problèmes avec ce type de test.

Cela prend trop de temps

Mon temps est sûrement mieux dépensé pour développer la prochaine chose super cool.

C’est exagéré

Je dirais que c’est acceptable si certaines des fonctions de niveau moyen à faible ne satisfont pas vraiment leur interface interne complète.
Tant que ces carences ne génèrent pas d’erreurs au niveau de l’API, les utilisateurs n’auront jamais de petits bogues dans les fonctions auxiliaires.

Pour chaque fonction interne de votre logiciel, l'ensemble des entrées valides possibles pour la fonction est plus grand que l'ensemble que votre logiciel va réellement transmettre à la fonction.
C'est évident mais important.

Supposons que votre logiciel utilise parfois la fonction ‘pow ()’ pour calculer le volume cubique.
L’interface de ‘pow’ est ‘pow (x, y[,z]) ».
Votre logiciel uniquement chaque ensemble de y à 3 et ne remplit jamais z.
Si vous estimez avoir besoin de tester ‘pow ()’, vous n’avez pas besoin de vous soucier de tester une entrée z, et seulement 3 comme y est nécessaire.

Regardons un autre exemple.
Disons que j’ai une fonction interne qui prend une chaîne et en supprime les balises HTML.
Maintenant, pour le tester complètement en isolation, je devrais probablement au moins faire ce qui suit:

  • Testez-le en passant «Aucun»
  • Tester une chaîne vide
  • Tester une chaîne d'un caractère
  • Testez quelque chose de gros, comme une chaîne de 2 Mo.
  • Testez-le avec des chaînes unicode
  • Tester les balises XML ou d'autres balises non HTML
  • Testez plusieurs niveaux de tags
  • Testez les balises qui ne correspondent pas.

    sans

    , etc

  • Tester des chaînes avec des retours à la ligne aléatoires insérés

Je peux voir maintenant que je vais probablement passer plus de temps à écrire des tests pour ‘tag_strip ()’ qu’au temps nécessaire pour écrire la fonction. Toutefois, si ‘tag_strip ()’ fait partie de mon API, c’est du temps bien dépensé.
Mais ‘tag_strip ()’ ne fait pas partie de l’API. C’est une fonction interne.
Et lors de l'inspection du logiciel final, je pourrais remarquer que mon logiciel ne supprime jamais les balises HTML des chaînes qu'il génère.
Et je ne l'utilise jamais que pour les titres.

Interface réelle pour tag_strip ():

  • tag_strip ('

    salut

    ') -> «Bonjour»

  • tag_strip ('

    Foo

    ') -> 'Foo'

  • tag_strip ('

    ') -> ”

Alors:

  • Je ne passe jamais dans aucun
  • Je ne passe jamais la chaîne vide
  • Je ne passe jamais dans une chaîne de caractères
  • Je ne passe jamais dans les grosses cordes
  • Je ne passe jamais en unicode
  • Je ne passe jamais dans les balises XML ou d'autres balises non HTML
  • Je ne passe jamais dans les chaînes multilignes

En fin de compte, j’aurai peut-être une fonction ‘tag_strip ()’ très robuste.
Mais c’est bien fini.

C’est inflexible

Si absolument tous les composants de votre logiciel sont entourés d'un test, toute refonte du code, tout changement de code, fera probablement échouer bon nombre de vos tests. Vous devrez revenir en arrière et examiner si le code de test est incorrect ou si votre nouveau code est incorrect. Et vous devrez écrire de nouveaux tests pour accompagner le nouveau code.

Essayez-le parfois. C’est une façon écrasante d’écrire un logiciel. Et ce n’est pas amusant.

De nombreux changements importants dans la conception et la mise en œuvre seront ignorés en raison de cette rigidité et des frais généraux liés à tout changement.

C’est une illusion d’essais complets

Il y a tellement de tests dans le système que beaucoup de gens penseront que la couverture des tests est complète.
Cependant, une couverture complète n'est tout simplement pas possible.

La couverture complète de toutes les entrées ET combinaisons possibles des entrées ET des combinaisons de l'ordre des appels de méthode est JUSTE POSSIBLE.
Sauf si vous fournissez une API avec une fonction qui ne prend aucune entrée.
Chaque fonction que vous ajoutez et chaque paramètre d’entrée étend de manière exponentielle les combinaisons d’entrées possibles pour votre système.

Vous ne pouvez pas effectuer de tests exhaustifs complets de votre système.
Si vous pensiez pouvoir le faire, surmontez-vous.
Ce n'est pas possible.

Que devrions nous faire?

Tout espoir n'est pas perdu. Et tester est une chose merveilleuse.
Ne jette pas encore la serviette.

Avant de donner mon avis sur la manière dont les tests doivent être effectués, je vais aborder au moins quelques approches supplémentaires qui, à mon avis, paraissent raisonnables, mais qui posent de graves problèmes.
Et oui, je ne peux que présenter mon avis.
Je ne pense pas qu’il en existe un La bonne façon tester.

Cependant, je pense qu’il est utile d’examiner certaines des solutions sérieusement envisagées. faux.