Tests unitaires - Application couplée à la base de données


15

Quelle serait la meilleure approche pour tester à l'unité un modèle qui s'intègre dans une application étroitement couplée à une base de données?

Le scénario spécifique ici est un panier d'achat - j'aimerais pouvoir tester l'ajout, la suppression et la récupération d'articles du panier ainsi que la logique de tarification, etc. l'accès à la base de données doit être évité.


1
Intéressant que les réponses qui disent effectivement "réécrivez le code de votre application" soient votées
AD7six

Réponses:


10

L'injection de dépendance est une façon de gérer cela. Vous pouvez configurer une base de données de test pour imiter le panier d'achat, ou vous pouvez même écrire un code qui "confirme" la transaction du client. Ensuite, lors de l'exécution, votre logiciel choisira le composant auquel se connecter.

Ne vous connectez à la base de données de production pour rien pendant les tests!


1
Avec DI et une conception d'application appropriée, vous devriez pouvoir tester sans aucune base de données --- à condition que la maquette que vous injectez fournisse une simulation suffisamment détaillée de la base de données principale.
Peter K.

4

Dans le test unitaire, vous devez définir la limite de ce que vous testez. Les tests unitaires sont différents des tests d'intégration. Si la logique de tarification est indépendante du contenu du panier, vous testez cela séparément. Si ce n'est pas le cas et que tous les modules sont étroitement couplés, créez un environnement de test qui imite autant que possible la production et travaillez avec cela. Je ne pense pas que les raccourcis et la simulation soient utiles à long terme.


2

Le modèle ne doit pas dépendre d'une base de données (concrète). S'il ne connaît qu'une base de données abstraite (lire "interface") qui est remise au modèle, vous pouvez remplacer la base de données par un objet factice .

Dans la programmation orientée objet , les objets fictifs sont des objets simulés qui imitent le comportement d'objets réels de manière contrôlée. Un programmeur crée généralement un objet simulé pour tester le comportement d'un autre objet, de la même manière qu'un concepteur de voitures utilise un mannequin de crash test pour simuler le comportement dynamique d'un humain dans les impacts de véhicules ...


1

J'ai eu un problème similaire - je n'avais aucune possibilité de garantir que ma base de données de test conserve les valeurs. Donc, à l'avenir, je reçois par exemple d'autres prix.

J'ai extrait les données dont j'avais besoin dans un petit sqlite -DB et utilisé cette base de données pour mes tests. Le Test-DB fait maintenant partie de la configuration de mon test unitaire.


2
Le point des tests unitaires est de tester votre code de manière isolée. Si vous utilisez une base de données sqllite, ce n'est pas isolé. Des incohérences entre les bases de données peuvent également provoquer des erreurs
Tom Squires

0

"Best" est subjectif, mais vous pouvez simplement utiliser une connexion de test DB.

Utilisez des appareils pour charger des données de test (exemples de produits à acheter), puis écrivez le scénario de test pour la classe / fonction que vous souhaitez tester.


Décrire les tests unitaires qui testent une fonction qui agit sur une base de données comme des tests d'intégration est assez trompeur @murph.
AD7six

1
Ok, maintenant je suis profondément confus - si cela implique une base de données ce n'est pas par la plupart des définitions un test unitaire parce que ce n'est pas autonome. Si vous avez une base de données, vous exécutez des tests à un niveau supérieur, celui qui avait des dépendances, celui qui examine la "combinaison" de choses. Quoi qu'il en soit, ce n'est pas une explication claire pour moi de la façon de résoudre le problème.
Murph

0

J'ai construit un plugin pour Symfony 1.4 (PHP) pour résoudre ce problème (entre autres). Il est modélisé d'après le fonctionnement du framework de test de Django (Python) : le framework construit et remplit une base de données de test distincte avant le début de chaque test, et il détruit la base de données de test une fois chaque test terminé.

J'avais quelques inquiétudes à propos de cette stratégie, à la fois en termes de performances (si le schéma ne change pas, pourquoi ne pas simplement effacer les données au lieu de reconstruire toute la structure?) Et de commodité (parfois, je veux inspecter la base de données après un échec du test, alors ne le détruisez pas aveuglément!), j'ai donc adopté une approche légèrement différente.

Avant le premier test, la base de données est détruite et reconstruite, au cas où il y aurait eu des changements de modèle depuis le dernier test. Avant chaque exécution de test suivante, les données de la base de données sont effacées, mais la structure n'est pas reconstruite (bien qu'une reconstruction manuelle puisse être déclenchée à partir d'un test si nécessaire).

En chargeant sélectivement les appareils de données dans chaque test, on peut créer l'environnement approprié pour ce test sans interférer avec les tests suivants. Les fichiers de fixture peuvent également être réutilisés, ce qui rend cette tâche beaucoup moins onéreuse (bien que ce soit toujours ma partie la moins préférée des tests d'écriture!).

Dans les deux cadres de test, l'adaptateur de base de données est configuré pour utiliser la connexion de test au lieu de la connexion "de production" pour empêcher l'exécution du test de corrompre les données existantes.


0

Je dirais, allez-y et utilisez des appareils pour pré-charger les données. C'est ainsi que les frameworks de tests unitaires semblent fonctionner en général, lors du test de manipulation des données.

Mais si vous voulez vraiment éviter de vous connecter à une base de données de quelque sorte que ce soit et de suivre la définition trop stricte selon laquelle les tests unitaires ne touchent à rien en dehors du code, jetez un œil à la simulation d'objets - cela peut vous donner des idées.

Par exemple, au lieu de déposer le SQL directement dans le code où vous en avez besoin, ayez un moyen d'appeler une méthode qui ne fait que ce que fait SQL. Utilisez Person.getPhoneNumber(), par exemple, au lieu de SELECT phone_number FROM person WHERE id = <foo>. Non seulement il est plus propre et plus facile à comprendre en un coup d'œil, mais pendant les tests, vous pouvez vous moquer de l'objet Personne afin qu'il getPhoneNumber()revienne toujours 555-555-5555ou quelque chose, au lieu de toucher la base de données.


0

C'est assez facile à faire avec la junit si elle est un peu longue.

La "configuration" doit définir et remplir un ensemble de tables temporaires.

Vous pouvez ensuite effectuer les tests unitaires pour toutes les fonctionnalités de mise à jour, d'insertion et de suppression.

Pour chaque test, vous appelez votre méthode de mise à jour, puis exécutez du SQL pour vérifier le résultat attendu.

Dans la phase de "démontage", vous supprimez toutes les tables.

De cette façon, vous exécutez toujours les mêmes tests sur les mêmes données initiales. Si vous conservez les tables entre les tests, ils finissent par être "pollués" par les tests ayant échoué, un test "d'insertion" cohérent est presque impossible car vous devez continuer à inventer de nouvelles clés à chaque test.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.