Il existe, quelque part dans votre base de code, une ligne de code qui effectue l'action réelle de connexion à la base de données distante. Cette ligne de code est, 9 fois sur 10, un appel à une méthode "intégrée" fournie par les bibliothèques d'exécution spécifiques à votre langue et à votre environnement. En tant que tel, ce n'est pas "votre" code et vous n'avez donc pas besoin de le tester; aux fins d'un test unitaire, vous pouvez être sûr que cet appel de méthode fonctionnera correctement. Ce que vous pouvez et devriez toujours tester dans votre suite de tests unitaires, par exemple, vous assurer que les paramètres qui seront utilisés pour cet appel correspondent à ce que vous attendez, comme s’assurer que la chaîne de connexion est correcte, ou l’instruction SQL ou nom de procédure stockée.
C’est l’un des objectifs de la restriction selon laquelle les tests unitaires ne doivent pas quitter leur "bac à sable" d’exécution et dépendent de l’état externe. C'est en fait assez pratique; le but d'un test unitaire est de vérifier que le code que vous avez écrit (ou êtes sur le point d'écrire, en TDD) se comporte comme vous le pensiez. Le code que vous n'avez pas écrit, tel que la bibliothèque que vous utilisez pour effectuer vos opérations de base de données, ne devrait pas faire partie de la portée d'un test unitaire, pour la simple raison que vous ne l'avez pas écrit.
Dans votre suite de tests d' intégration , ces restrictions sont assouplies. Maintenant vous pouvezDes tests de conception qui touchent la base de données, pour vous assurer que le code que vous avez écrit joue bien avec du code que vous n'avez pas. Cependant, ces deux suites de tests doivent rester séparées, car votre suite de tests unitaires est d'autant plus efficace qu'elle s'exécute rapidement (vous pouvez ainsi vérifier rapidement que toutes les affirmations des développeurs concernant leur code sont toujours valables) et, presque par définition, un test d'intégration. est plus lent par ordres de grandeur en raison des dépendances supplémentaires sur les ressources externes. Laissez-le-robot gérer l'exécution de votre suite d'intégration complète toutes les quelques heures, en exécutant les tests qui bloquent les ressources externes, afin que les développeurs ne se marchent pas l'un sur l'autre en exécutant ces mêmes tests localement. Et si la construction casse, et alors? Il est beaucoup plus important de s’assurer que le build-bot n’échoue jamais une construction comme il se doit.
Maintenant, le degré de stricte conformité avec cela dépend de votre stratégie exacte pour vous connecter à la base de données et l'interroger. Dans de nombreux cas où vous devez utiliser la structure d'accès aux données "à l'état brut", telle que les objets SqlConnection et SqlStatement d'ADO.NET, une méthode complète que vous avez développée peut être constituée d'appels de méthode intégrés et d'un autre code dépendant de la présence d'un objet. connexion de base de données, et le mieux que vous puissiez faire dans cette situation est de simuler toute la fonction et de faire confiance à vos suites de tests d’intégration. Cela dépend également de votre volonté de concevoir vos classes afin de permettre le remplacement de lignes de code spécifiques à des fins de test (comme la suggestion de Tobi concernant le modèle de méthode, qui est un bon modèle car il permet des "simulations partielles".
Si votre modèle de persistance des données repose sur du code de votre couche de données (tels que des déclencheurs, des processus stockés, etc.), il n’existe tout simplement pas d’autre moyen d’exercer du code que vous écrivez vous-même que de développer des tests qui vivent dans la couche de données ou qui traversent la couche de données. limite entre le runtime de votre application et le SGBD. Un puriste dirait que ce modèle, pour cette raison, doit être évité en faveur de quelque chose comme un ORM. Je ne pense pas que j'irais aussi loin; Même à l'ère des requêtes intégrées dans la langue et des autres opérations de persistance dépendantes du domaine et vérifiées par le compilateur, je vois l'intérêt de verrouiller la base de données sur les seules opérations exposées via une procédure stockée. Bien entendu, ces procédures stockées doivent être vérifiées à l'aide de la méthode automatisée. tests. Mais, ces tests ne sont pas des tests unitaires . Ils sont l' intégration tests.
Si vous rencontrez un problème avec cette distinction, elle est généralement basée sur une grande importance accordée à la "couverture de code" complète, à savoir "couverture de test unitaire". Vous voulez vous assurer que chaque ligne de votre code est couverte par un test unitaire. Un objectif noble, mais je dis foutaise; cette mentalité se prête bien à des anti-schémas qui vont bien au-delà de ce cas particulier, tels que la rédaction de tests sans assertion qui s'exécutent sans exercervotre code. Ces types de fin d’année uniquement à des fins de couverture sont plus dommageables que d’assouplir votre couverture minimale. Si vous voulez vous assurer que chaque ligne de votre base de code est exécutée par un test automatisé, alors c'est facile; lors du calcul des métriques de couverture de code, incluez les tests d'intégration. Vous pouvez même aller un peu plus loin et isoler ces tests "Itino" contestés ("Intégration dans le nom uniquement"), et entre votre suite de tests unitaires et cette sous-catégorie de tests d'intégration (qui devrait néanmoins fonctionner assez rapidement), vous devriez proche de la couverture complète.