Évaluer s'il faut d'abord écrire des tests unitaires ou des tests d'intégration sur des projets de ciel bleu / prototypes

11

Quelque chose que j'ai remarqué récemment, c'est quand je fais les types de projets suivants:

  • Au début d'un projet
  • Travailler sur un MVP / prototype
  • Ajout de fonctionnalités qui ne sont pas entièrement définies
  • Travailler sur un projet à plus petite échelle

Pour référence, je travaille actuellement sur un projet Python qui contient actuellement environ 1k lignes de code, y compris certains commentaires et tous les espaces.

Je trouve qu'il est extrêmement facile d'écrire d'abord des tests d'intégration, de travailler sur le code, puis une fois que l'API est quelque peu durcie, travailler sur l'ajout de tests unitaires. Les types de tests que je peux exécuter sur ma mainfonction, pour ainsi dire, et sont plus "de bout en bout" qu'autre chose.

En effet, les tests unitaires sont vraiment ennuyeux lorsqu'une API change assez rapidement, ce qui est souvent le cas lorsque vous travaillez sur un projet correspondant à l'un ou à la plupart des critères ci-dessus.

Cette approche est-elle une bonne approche et quels sont les critères à prendre en compte pour décider de commencer par des tests unitaires ou d'intégration en premier pour ces types de projets? Est-ce que je manque la valeur des tests unitaires de ces types de projets avant que les API ne soient plus solides?

enderland
la source
6
Faites ce qui vous convient le mieux. N'écoutez pas les gens qui disent que vous devez travailler d'une certaine manière pour être efficace: vous savez quand vous êtes efficace et quand vous ne l'êtes pas. Que vous écriviez d'abord les tests d'intégration ou les tests unitaires, cela n'a pas vraiment d'importance. Pour certains projets, une voie peut être plus facile et pour d'autres, l'autre. Ce que vous décrivez peut être la différence entre une conception descendante et ascendante. Les deux sont utiles, mais de haut en bas produisent généralement de meilleurs designs.
Frank Hileman
@FrankHileman en effet, c'est mon approche. Mais comme je suis curieux, je veux m'assurer de faire la bonne approche au cas où je manquerais quelque chose.
enderland
Concentrez-vous d'abord sur les spécifications: les parties non codées. Quels sont les invariants du système? Ce faisant, vous pouvez trouver que vous devez d'abord déterminer le niveau bas ou le niveau haut. Cela dépend de l'emplacement des algorithmes les plus critiques ou les plus risqués. Essayez d'abord de les éliminer. C'est la gestion de base des risques.
Frank Hileman
1
En cas de travail sur un prototype, il est normal de ne pas écrire de tests du tout. Le but du prototype est de vérifier l'idée de travail. La mise en œuvre du prototype aidera à reconnaître la conception attendue de l'application.
Fabio
C'est ce qu'on appelle le développement extérieur-intérieur. Vous voudrez peut-être consulter le livre suivant, qui fait exactement cela: amazon.com/dp/0321503627
Eternal21

Réponses:

7

Est-ce que je manque la valeur des tests unitaires de ces types de projets avant que les API ne soient plus solides?

Non, tu vas bien.

Les deux grands objectifs du TDD sont:

  • Définition des interfaces par utilisation réelle, plutôt que par implémentation interne 1
  • Maximiser la couverture des tests

La couverture des tests peut être assez bien maximisée dans tous les cas. Autrement dit, que vous testiez d'abord de petites unités isolées ou de grandes unités «intégrées», vous avez la possibilité d'écrire vos tests avant vos implémentations.

Ce que vous gagnez en écrivant des tests de niveau supérieur («intégration») en premier lieu, comme vous le faites, c'est l'assurance que vos interfaces et interactions de niveau supérieur sont également définies principalement en fonction de leur utilisation, plutôt que par leurs implémentations internes.

Le même effet peut être obtenu en grande partie avec une bonne "architecture" et un bon diagramme. Mais, ces tests de haut niveau peuvent souvent révéler des choses que vous avez manquées dans vos diagrammes - ou que vous vous êtes juste trompés dans votre travail «d'architecture».


Si vous ne faites pas de TDD (ou quelque chose du genre), l'ordre dans lequel vous écrivez les tests n'a pas beaucoup d'importance. Les interfaces existent déjà au moment où vous testez, il est donc beaucoup moins probable que vos tests changent quoi que ce soit - ils ne serviront qu'à vous protéger contre des changements de rupture particuliers.

Mais, si vous souhaitez construire l'implémentation de haut en bas ou de bout en bout, la première puce s'applique toujours dans une large mesure. Le code de haut niveau permet de définir des interfaces de bas niveau. Alors que, si les interfaces de bas niveau sont écrites en premier (ou sinon existent déjà), le code de haut niveau est à leur merci ...


1. Celui-ci s'applique également même si vous ne faites pas de TDD complet. Même si vous écrivez juste 1 ou 2 tests avant votre implémentation, ces 1 ou 2 tests peuvent vous aider à définir ou affiner vos interfaces avant qu'il ne soit trop tard!

svidgen
la source
1

J'ai travaillé comme tu travailles. Et je ne vais pas vous dire que vous ne pouvez pas. Je vais vous avertir de quelque chose que vous pourriez rencontrer.

Lorsque chaque test unitaire est simplement une modification, il est difficile d'apprendre à les rendre flexibles. Ils ont tendance à n'être rien de plus que des tests de régression. Comme vous ne les avez jamais utilisés pour vous aider à refactoriser, il est très facile d'écrire les types de tests qui rendent la refactorisation plus difficile. Cela a tendance à devenir incontrôlable jusqu'à ce que vous perdiez toute confiance en TDD.

Cependant, vous travaillez déjà sur quelque chose. Je ne vais pas te dire d'arrêter. Je dirai que cela vaut la peine de commencer autre chose que vous avez le temps d'explorer et de suivre le cycle de refactorisation rouge vert depuis le début. Assurez-vous que vous utilisez réellement les tests pour vous aider à refactoriser. Jusqu'à ce que vous maîtrisiez cette façon de travailler, utilisez-la avec parcimonie sur quelque chose qui compte. C'est une façon très différente de coder et il faut s'y habituer. Le faire à mi-chemin ne fera du bien à personne.

Cela dit

Je trouve qu'il est extrêmement facile d'écrire d'abord des tests d'intégration, de travailler sur le code, puis une fois que l'API est quelque peu durcie, travailler sur l'ajout de tests unitaires. Les types de tests que je peux exécuter sur ma fonction principale, pour ainsi dire, sont plus "de bout en bout" qu'autre chose.

Comprenez qu'un test unitaire n'est PAS simplement un test qui agit sur une classe. Tant que l'API sur laquelle vous travaillez peut être testée sans effectuer l'une des opérations suivantes, vous effectuez très bien les tests unitaires:

  • Il parle à la base de données
  • Il communique à travers le réseau
  • Il touche le système de fichiers
  • Il ne peut pas fonctionner en même temps que n'importe lequel de vos autres tests unitaires
  • Vous devez faire des choses spéciales à votre environnement (comme éditer des fichiers de configuration) pour l'exécuter.

Michael Feathers: un ensemble de règles de tests unitaires

Donc, si votre test de bout en bout implique plus d'un objet, ça va. Il s'agit de tests unitaires et non de tests d'objets.

Tout comme les méthodes privées n'ont plus besoin d'être testées, elles sont testées en testant les méthodes publiques qui les utilisent, les objets n'ont pas besoin d'être initialement développés sous leur propre harnais de test. Ce n'est que lorsque les objets sont envisagés pour une utilisation indépendante de l'histoire de bout en bout qu'ils doivent vraiment être traités comme s'ils avaient leur propre interface et comportement à confirmer. Si vous faites attention à ce sujet, c'est lorsque vous rendez ces objets publics. De cette façon, vous ne faites aucune promesse que vous n'avez pas testée.

candied_orange
la source