Bases de données et tests unitaires / d'intégration

25

J'ai eu une discussion avec quelqu'un sur les tests unitaires / d'intégration avec les applications Web et j'ai un désaccord sur 1 idée principale. Le problème est que la personne à qui je parle pense que la base de données à partir de laquelle le test unitaire fonctionne doit contenir des données préremplies et je pense qu'elle doit être complètement vide avant et après l'exécution des tests.

Ma préoccupation avec les données préremplies dans la base de données est qu'il n'y a aucun moyen de s'assurer que les données sont maintenues en bon état. Les tests eux-mêmes vont créer, supprimer et modifier des données dans la base de données, donc je ne vois vraiment pas comment avoir des données dans la base de données avant de commencer les tests est une bonne chose.

Il semble que la meilleure façon de tester la fonctionnalité de la base de données serait d'avoir les configurations suivantes:

  1. Dans une phase de "configuration" avant l'exécution du test, vous tronquez d'abord toutes les tables de la base de données
  2. Ensuite, vous insérez toutes les données nécessaires pour les cas de test que vous êtes sur le point d'exécuter
  3. Ensuite, vous exécutez et validez les cas de test
  4. Ensuite, dans une phase de "démontage", vous tronquez à nouveau toutes les tables de la base de données

Je ne vois pas d'autre meilleur moyen de garantir que les données que vous testez sont un bon test testable.

Est-ce que j'ai râté quelque chose? N'est-ce pas la meilleure façon de tester les fonctionnalités liées à la base de données? Y a-t-il un avantage à avoir une base de données préremplie qui existe toujours dans la base de données (même avant de commencer les tests ou après les tests)? Toute aide dans les idées pour expliquer mon processus différemment afin de mieux faire passer mon message serait également formidable (c'est-à-dire si mon point a des mérites).

ryanzec
la source
voir aussi: Software Testing Techniques
gnat

Réponses:

21

Pour moi, les tests unitaires ne doivent pas porter sur la base de données, les tests d'intégration concernent la base de données.

Les tests d'intégration qui traitent de la base de données devraient en pratique avoir une base de données vide avec une approche de démontage et de démontage, l'utilisation d'une approche basée sur les transactions est tout à fait une bonne façon de procéder (c'est-à-dire créer une transaction lors de la configuration et revenir en arrière lors du démontage).

Ce que votre ami sonne comme s'il voulait faire, c'est tester d'un point de vue de «régression», c'est-à-dire y avoir des données réelles et voir comment le système réagit, après tout aucun système n'est parfait et il peut généralement y avoir de mauvaises données quelque part qui fournissent quelques bizarreries à votre modèle de domaine.

Vos meilleures pratiques sont la voie à suivre, et ce que j'ai tendance à faire, c'est si je trouve un scénario de mauvaises données, écrivez un test d'intégration avec une configuration et démontez avec ce scénario exact.

Nicholas Mayne
la source
Je suis juste sûr de savoir quelle est la différence entre les tests unitaires et les tests d'intégration, en plus d'entendre que l'unité doit utiliser des données simulées et que l'intégration doit utiliser une base de données (a démarré un autre thread programmers.stackexchange.com/questions/101300/… pour comprendre la différence ). À part cela, tout ce que vous dites semble correspondre à ce que je pense.
ryanzec
Pas de problème, j'ai ajouté plus d'informations à votre autre réponse
Nicholas Mayne
1
pourquoi ne pouvez-vous pas tester la DB à l'unité? Si vous placez votre SQL dans des procédures stockées, vous pouvez les tester à l'unité avec des données définies par le test, et soudain, tout est facile. C'est certainement une meilleure pratique que plus de gens devraient suivre, voir ce que dit MS
gbjbaanb
1
integration tests- Que voulez-vous dire? Comme je l'ai mentionné, les modules qui utilisent la base de données peuvent et doivent être testés avec des tests unitaires. La base de données peut être moquée manuellement ou remplacée par une implémentation en mémoire
hellboy
6

Si vos tests dépendent de la base de données, je pense qu'il est plus important que les données qui vous intéressent soient dans un état connu pour vos tests, plutôt que la base de données soit vide. L'une des mesures de bons tests est que chaque test doit échouer pour une raison et qu'aucun autre test ne doit échouer pour cette même raison.

Donc, si vos tests se soucient de l'état des données, mettez-les dans cet état connu et remettez-les dans cet état après l'exécution de vos tests, afin que vos tests soient reproductibles.

Si vous pouvez dissocier vos tests de l'état des données en vous moquant, ce serait également une bonne chose. Vous mentionnez que vous faites des tests unitaires / d'intégration, mais bien sûr, ces deux choses doivent être considérées séparément. Vos tests unitaires doivent être dissociés de la base de données si possible et vos tests d'intégration doivent être testés avec la base de données dans un état connu.

Paddyslacker
la source
2

Eh bien, je vois un avantage à avoir une base de données préremplie: vous n'avez pas à écrire le code qui insérera les données dont vous avez besoin, car il est là. Sinon, il n'y a que des inconvénients. Peut-être que quelqu'un a modifié les données de test sur la base de données? Peut-être que quelqu'un a tenté de rafraîchir les données? Mais le pire, c'est qu'un cas de test gâche mal la base de données ... Vous finissez par recréer manuellement la base de données entière plusieurs fois.

Vous avez raison sur la façon dont les tests doivent être écrits, sauf que je ne tronquerais rien:

  • phase de configuration: obtenir une connexion à la base de données et insérer les données
  • phase d'exécution
  • phase de démontage: supprimer les données insérées (tronquer)

Maintenant, ce scénario est idéal pour les tests unitaires. Lorsque l'on a besoin de données pour les tests unitaires et d'intégration, j'ai trouvé qu'une grande phase de configuration commune à tous les cas de test (nous avons regroupé toutes les "insertions" dans une méthode statique) peut également très bien fonctionner. C'est comme un juste milieu entre votre idée et celle de votre ami. Le seul inconvénient est que vous devez être très prudent lors de l'ajout de nouvelles données afin de ne pas casser les cas de test existants (mais si vous ajoutez comme deux ou trois lignes par table comme nous l'avons fait, cela ne devrait pas être un problème)

Jalayn
la source
Ne préférerais-je pas créer les parties de la base de données nécessaires pour le test plutôt que de modifier accidentellement les données de manière à provoquer un échec? Devoir s'assurer que les données sont correctes lorsqu'un test échoue semble être quelque chose qui peut être évité.
ryanzec
1
La grande phase de configuration qui insère des données utiles à différents cas de test ne peut être utile que pour les tests d'intégration où vous devez vérifier que différentes parties de l'application fonctionnent ensemble. Il peut être utile d'avoir ce grand ensemble commun d '"insertions" car vous en aurez probablement besoin pour d'autres tests d'intégration. Sinon, si nous parlons uniquement de tests unitaires purs, je suis absolument pour avoir un ensemble de données à insérer pour chaque cas de test.
Jalayn
1

Je pense que vous devez préciser un exemple avec votre collègue et savoir ce qu'ils signifient exactement. Vous pouvez être tous les deux sur la même page.

Exemple: vérification de la table des transactions du compte

  1. Souhaitez-vous pas tester l'affichage de ce tableau pour un utilisateur / compte sans transactions?
  2. Testez l'ajout du premier enregistrement et voyez si vous pouvez créer un équilibre.
  3. Créez des enregistrements lorsqu'il existe déjà des enregistrements et vérifiez le solde en cours et toutes les autres règles métier.
  4. Voir le tableau avec les enregistrements existants et tous les autres CRUD.

Que vous y parveniez en exécutant les étapes 1 et 2 ou en commençant par une base de données déjà dans cet état (restaurer une sauvegarde?), Je ne suis pas sûr que cela soit important. Votre idée de m'en faire un script facilite la gestion des modifications dont vous avez besoin (comme si vous avez oublié de créer un compte administrateur et que vous en avez besoin pour un nouvel utilisateur). Les fichiers de script sont plus faciles à placer dans le contrôle de code source que certains fichiers de sauvegarde. Cela dépend également de la distribution ou non de cette application.

JeffO
la source
0

Pour dessiner les aspects de quelques réponses ensemble et ajouter mon 2p ...

Remarque: mes commentaires concernent les tests de base de données en particulier , et non les tests d'interface utilisateur (bien que de toute évidence similaire s'applique).

Les bases de données ont tout autant besoin de tests que les applications frontales, mais ont tendance à être testées sur la base de "cela fonctionne-t-il avec le front-end?" ou "les rapports produisent-ils le résultat correct?", qui à mon avis est testé très tard dans le processus de développement de la base de données et pas très robuste.

Nous avons un certain nombre de clients qui utilisent des tests unitaires / d'intégration / système pour leur base de données d'entrepôt de données en plus de l'UAT / performance / et al. tests. Ils constatent qu'avec une intégration continue et des tests automatisés, ils détectent de nombreux problèmes avant de passer à l'UAT traditionnel, économisant ainsi du temps dans l'UAT et augmentant les chances de succès de l'UAT.

Je suis sûr que la plupart conviendraient qu'une rigueur similaire devrait être appliquée aux tests de base de données comme aux tests frontaux ou de rapport.

L'essentiel avec les tests est de tester de petites entités simples, en s'assurant de leur exactitude, avant de procéder à des combinaisons complexes d'entités, en garantissant leur exactitude avant de s'étendre au système plus large.

Pour donner un peu de contexte à ma réponse ...

Tests unitaires

  • a un objectif de test pour prouver que l'unité fonctionne, par exemple une table, une vue, une fonction, une procédure stockée
  • devrait 'stub' les interfaces pour supprimer les dépendances externes
  • fournira ses propres données. Vous avez besoin d'un état de départ connu des données, donc s'il y a une chance de pré-test des données existantes, alors des troncatures / suppressions devraient se produire avant le remplissage
  • fonctionnera idéalement dans son propre contexte d'exécution
  • s'effacera après lui-même et supprimera les données qu'il a utilisées; ceci n'est important que lorsque les talons ne sont pas utilisés.

Les avantages de cette opération sont que vous supprimez toutes les dépendances externes sur le test et effectuez la plus petite quantité de tests pour prouver l'exactitude. De toute évidence, ces tests ne peuvent pas être exécutés sur la base de données de production. Il se peut qu'il existe un certain nombre de types de tests que vous ferez, selon le type d'unité, notamment:

  • vérification du schéma, certains pourraient appeler cela un test de «contrat de données»
  • valeurs de colonne passant
  • l'exercice de chemins logiques avec différentes valeurs de données pour les fonctions, procédures, vues, colonnes calculées
  • test de cas de bord - NULL, mauvaises données, nombres négatifs, valeurs trop grandes

(Unité) Test d'intégration

J'ai trouvé ce post SE utile pour parler de différents types de tests.

  • a pour objectif de tester pour prouver que les unités s'intègrent ensemble
  • effectué sur un certain nombre d'unités ensemble
  • devrait 'stub' les interfaces pour supprimer les dépendances externes
  • fournira ses propres données, pour supprimer les effets des influences des données externes
  • fonctionnera idéalement dans son propre contexte d'exécution
  • disparaîtra après lui-même et supprimera les données créées; ceci n'est important que lorsque les talons ne sont pas utilisés.

En passant des tests unitaires à ces tests d'intégration, il y aura souvent un peu plus de données, afin de tester une plus grande variété de cas de test. De toute évidence, ces tests ne peuvent pas être exécutés sur la base de données de production.

Ce procède ensuite sur système de test , tests d' intégration système (aka tests de bout en bout 2), avec l' augmentation des volumes de données et l' augmentation de la portée. Tous ces tests devraient faire partie d'un cadre de tests de régression. Certains de ces tests peuvent être choisis par les utilisateurs pour être exécutés dans le cadre de l'UAT, mais UAT est les tests définis par les utilisateurs , pas tels que définis par l'informatique - un problème courant!

Alors maintenant que j'ai donné un certain contexte, pour répondre à vos vraies questions

  • le préremplissage des données pour les tests unitaires et d'intégration peut provoquer de fausses erreurs de test et doit être évité.
  • La seule façon d'assurer des tests cohérents est de ne faire aucune hypothèse sur les données source et de les contrôler rigoureusement.
  • un contexte d'exécution de test distinct est important, pour garantir qu'un testeur n'entre pas en conflit avec un autre testeur effectuant les mêmes tests sur une branche différente du code de base de données contrôlé par la source.
Marcus D
la source
-3

Franchement, je pense que si vous faites des tests unitaires sans une base de données à peu près de la même taille que la base de données de production existante, vous allez avoir beaucoup de choses qui passent les tests et échouent en production pour des performances. Bien sûr, je suis contre le développement de personnes sur une petite base de données locale pour cette raison également.

Et si le code est spécifique aux données, comment pouvez-vous le tester efficacement sans données? Vous ne verrez pas si les requêtes ont renvoyé les résultats corrects. Pourquoi voudriez-vous même envisager de tester par rapport à une base de données vide, tout ce qui vous dit, c'est si la syntaxe est correcte et non si la requête est correcte. Cela me semble à courte vue. J'ai vu trop de choses qui s'exécutent et passent des tests qui sont catégoriquement erronés. Vous ne voulez pas trouver cela dans les tests unitaires? Je fais.

HLGEM
la source
Je ne suggère pas d'exécuter sur une base de données vide, si vous voyez l'étape 2, j'ai "Ensuite, vous insérez toutes les données nécessaires pour les cas de test que vous êtes sur le point d'exécuter". À propos du problème de performances, je ne pense pas que ce soit à cela que servent les tests unitaires, c'est-à-dire davantage de tests de charge. Test unitaire pour moi afin de vérifier que la logique de votre code fonctionne. si la logique fonctionne, elle fonctionnera pour 1 enregistrement ou 100 000 000 000 d'enregistrements des mêmes données de base (pensé que ce sera beaucoup plus lent).
ryanzec
Les requêtes de base de données ne sont pas seulement logiques et plus tôt vous découvrirez que cela ne fonctionnera pas en mieux. Ce qui fonctionne souvent pour 1 enregistrement expirera sur prod et le test unitaire devrait le montrer dès que possible.
HLGEM
Les tests unitaires et d'intégration concernent la fonctionnalité et non les performances, vous pouvez donc tester avec de petites quantités de données
user151019
Les tests unitaires ne doivent jamais utiliser de base de données - les tests d'intégration utilisent des bases de données.
Nicholas Mayne
1
Vous parlez en fait de tests de charge. Si vous aviez un ensemble de tests d'acceptation et les connectiez à un outil de test de charge, vous seriez alors en mesure d'obtenir l'effet souhaité.
Nicholas Mayne