Je commence un nouveau projet et j'essaie très fort d'utiliser TDD pour piloter la conception. Je pousse depuis des années et j'ai finalement obtenu l'autorisation de consacrer plus de temps à ce projet pour l'utiliser pendant que j'apprends à le faire correctement.
Il s'agit d'un nouveau module, à relier à un système existant. Actuellement, tous les accès aux données se font par le biais de services Web, qui pour la plupart ne sont qu'une mince enveloppe sur les procédures stockées de la base de données.
Une exigence est que pour un magasin donné, je retourne tous les bons de commande considérés comme valides pour cette application. Un bon de commande est considéré comme valide si sa date d'expédition tombe avec une plage donnée à partir de la date d'ouverture des magasins (c'est pour les nouveaux magasins).
Maintenant, je ne peux pas mettre cette logique dans le code de l'application, car je ne vais pas ramener un million de bons de commande juste pour que la douzaine qui s'applique puisse s'appliquer à ce magasin compte tenu de la contrainte ci-dessus.
Je pensais, je pouvais passer la plage de dates à un proc GetValidPOs, et lui faire utiliser ces valeurs pour retourner les bons de commande valides. Mais que se passe-t-il si nous ajoutons une autre exigence à ce qui est considéré comme un bon de commande valide?
Et comment puis-je tester cela et vérifier qu'il fonctionne toujours? Nous n'utilisons pas d'ORM, et il est peu probable que cela se produise. Et je ne peux pas appeler la DB dans mon test.
Je suis coincé.
Mon autre pensée, est d'avoir des simulations qui retournent des données valides, d'autres qui retournent des données incorrectes, et que le référentiel local lève une exception si de mauvaises données se produisent, et teste que l'exception est levée si des données invalides sont renvoyées par GetValidPOs proc (ou la maquette utilisée dans les tests).
Est-ce que ça a du sens? Ou existe-t-il une meilleure façon?
MISE À JOUR: Je suis capable d'utiliser EF semble-t-il. Il me suffit maintenant de comprendre comment l'utiliser et de le rendre testable, tout en étant capable de s'appuyer sur des procédures stockées et la difficulté d'avoir des données dispersées sur plusieurs bases de données.
la source
Réponses:
Il s'agit d'un inconvénient majeur des procédures stockées à l'ère du TDD. Ils ont de réels avantages, même maintenant, mais par définition, tout test qui exerce un proc stocké n'est pas un test unitaire; c'est au mieux un test d'intégration.
La solution habituelle, en supposant que l'architecture ne peut pas changer pour utiliser un ORM à la place, est de ne pas placer ces tests dans la suite de tests unitaires; placez plutôt les tests dans une suite d'intégration. Vous pouvez toujours exécuter le test chaque fois que vous souhaitez vérifier qu'il fonctionne, mais parce que le coût inhérent à la configuration du test (initialisation d'une base de données avec les données de test appropriées) est élevé et qu'il touche des ressources, l'agent de test unitaire de votre build-bot peut ne pas avoir accès à, il ne devrait pas être dans la suite de tests unitaires.
Vous pouvez toujours tester le code unitaire qui nécessite les données, en faisant abstraction de tout ce que vous ne pouvez pas tester unitaire (classes ADO.NET) dans une classe DAO que vous pouvez ensuite simuler. Vous pouvez ensuite vérifier que les appels attendus sont effectués en consommant du code et reproduire le comportement du monde réel (par exemple, ne trouver aucun résultat) permettant de tester divers cas d'utilisation. Cependant, la configuration réelle de SqlCommand pour appeler le proc stocké est à peu près la toute dernière chose que vous pouvez tester unitaire, en séparant la création de commandes de l'exécution de commandes et en se moquant de l'exécuteur de commandes. Si cela ressemble à beaucoup de séparation des préoccupations, cela peut être; rappelez-vous, "il n'y a pas de problème qui ne peut pas être résolu par une autre couche d'indirection, sauf pour avoir trop de couches d'indirection". À un moment donné, vous devez dire "assez; je ne peux tout simplement pas le tester à l'unité, nous"
Autres options:
Testez le proc stocké à l'aide d'une instance de SGBD "de courte durée" comme SQLite. Il est généralement plus facile de le faire lors de l'utilisation d'un ORM, mais le test peut ensuite être effectué "en mémoire" (ou avec un fichier de base de données prédéfini inclus avec la suite de tests). Ce n'est toujours pas un test unitaire, mais il peut être exécuté avec un haut degré d'isolement (le SGBD fait partie du processus en cours, et pas quelque chose auquel vous vous connectez à distance qui peut être au milieu de la suite de tests conflictuelle de quelqu'un d'autre). L'inconvénient est que les modifications du processus stocké peuvent se produire en production sans que le test reflète le changement, vous devez donc être discipliné pour vous assurer que le changement est d'abord effectué dans un environnement de test.
Pensez à passer à un ORM. Un ORM avec un fournisseur Linq (pratiquement tous ceux couramment utilisés en ont un) vous permettrait de définir la requête comme une instruction Linq; cette instruction peut ensuite être donnée à un référentiel simulé qui a une collection en mémoire de données de test pour l'appliquer. Vous pouvez ainsi vérifier que la requête est correcte sans même toucher à la base de données (vous devez toujours exécuter la requête dans un environnement d'intégration, pour tester que le fournisseur Linq peut correctement digérer la requête).
la source
Mon conseil est de diviser pour mieux régner . Oubliez la base de données et la persistance pour le moment et concentrez-vous sur le test de fausses implémentations de vos référentiels ou objets d'accès aux données.
Je me moquerais du référentiel qui renvoie les bons de commande. Créez une maquette avec vingt bons de commande impairs.
Stub un appel à GetValidPOs afin qu'il appelle votre maquette, plutôt que la procédure de base de données.
Vous avez besoin d'un test unitaire pour vous assurer que les données correctes sont renvoyées à partir d'une maquette.
Vous avez également besoin d'un test d'intégration pour vous assurer que les données correctes sont renvoyées à partir d'une base de données. Le test d'intégration nécessiterait une configuration et un nettoyage. Par exemple, avant d'exécuter le test d'intégration, amorcez votre base de données en exécutant un script. Vérifiez que votre script a fonctionné. Recherchez la base de données en appelant vos procédures stockées. Vérifiez que vos résultats sont corrects. Nettoyez la base de données.
Comme je l'ai déjà dit, vous avez besoin d'une maquette qui renvoie au moins certaines données que vous pouvez interroger.
Lorsque vous interrogez des données, vous voulez vous assurer que votre système peut gérer les exceptions avec élégance. Par conséquent, vous vous moquez du comportement afin qu'il lève des exceptions dans certains scénarios. Vous écrivez ensuite des tests pour vous assurer que votre système peut gérer ces exceptions avec élégance.
la source
Tout comme le test unitaire Java ou Javascript signifie l'écriture de tests unitaires en utilisant le langage Java pour java et le test unitaire des fonctions Javascript avec Javascript, l'écriture de tests automatisés pour vous conduire à écrire des procédures stockées signifie que la bibliothèque de tests unitaires que vous recherchez est basée sur stockée procédures.
Autrement dit, utilisez des procédures stockées pour tester les procédures stockées car:
Tout comme TDD dans une langue OO, vous voulez que votre test unitaire ne configure qu'une ou deux lignes de données pour tester ce dont il a besoin pour la procédure (minimalisme, ne disposez que de ce dont vos tests simples ont besoin). Le résultat est que vous aurez plusieurs tests unitaires simples pour chaque procédure stockée. Ces tests simples seront plus faciles à maintenir que les tests compliqués qui dépendent d'un grand ensemble de données qui ne correspond pas facilement aux besoins réels du test.
la source