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é.
unit-testing
user1189880
la source
la source
Réponses:
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!
la source
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.
la source
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 .
la source
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.
la source
"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.
la source
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.
la source
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 deSELECT 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'ilgetPhoneNumber()
revienne toujours555-555-5555
ou quelque chose, au lieu de toucher la base de données.la source
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.
la source