Je suis vraiment confus quand je vois beaucoup d'implémentation de base de données en mémoire utilisée pour les tests, car j'ai également beaucoup entendu des meilleures pratiques de test d'intégration que l'environnement exécutant le test devrait ressembler le plus possible à l'environnement de production, y compris le système d'exploitation , bibliothèque, moteur de base de données, etc.
Qu'est-ce que j'oublie ici?
Réponses:
Dans une situation de développement logiciel typique, les tests sont utilisés à deux points: pendant le développement et avant de déplacer le produit le long de la chaîne de développement.
La première situation, exécuter des tests pendant le développement, sert des objectifs à court terme: définir des tâches (comme dans TDD: écrire un test qui échoue, puis le faire passer), empêcher les régressions, s'assurer que vos modifications ne cassent rien d'autre, etc. les tests doivent être extrêmement rapides: idéalement, toute votre suite de tests s'exécute en moins de 5 secondes, et vous pouvez simplement l'exécuter en boucle à côté de votre IDE ou éditeur de texte pendant que vous codez. Toute régression que vous introduisez apparaîtra en quelques secondes. Des tests rapides sont plus importants dans cette phase que la capture de 100% des régressions et des bugs, et comme il est impossible (ou carrément impossible) de développer sur des copies exactes des systèmes de production, l'effort requis pour obtenir des tests parfaits ici ne vaut pas la peine il. L'utilisation de bases de données en mémoire est un compromis: ce ne sont pas des copies exactes du système de production, mais ils aident à garder les essais sous la limite de 5 secondes; si le choix est entre une configuration de base de données légèrement différente pour mes tests liés à la base de données et aucun test du tout, je sais ce que je choisis.
La seconde situation, déplacer le code le long de la chaîne de développement, cependant, ne nécessitent des tests approfondis. Étant donné que nous pouvons (et devons) automatiser cette partie du processus de développement, nous pouvons nous permettre des tests beaucoup plus lents - même si un test complet prend des heures, la planification d'une construction nocturne signifie toujours que nous avons toujours une image précise de la base de code d'hier. Simuler l'environnement de production aussi précisément que possible est important maintenant, mais nous pouvons nous le permettre. Donc, nous ne faisons pas de compromis dans la base de données en mémoire: nous installons exactement la même version du même SGBD que les systèmes de production, et si possible, nous le remplissons avec des données de production réelles avant le début des tests.
la source
Je suppose que c'est une vitesse de compromis / correspondant à l'environnement. Les tests doivent être exécutés souvent, ce qui signifie qu'ils doivent être rapides. Surtout des tests unitaires, qui ne devraient pas prendre plus de quelques secondes.
Les tests d'intégration s'exécuteront plus lentement, mais lorsqu'ils sont rapides, vous pouvez les exécuter plus souvent. Par exemple, avant chaque commit. Bien sûr, ce n'est pas aussi complet qu'un environnement complet, mais au moins vous testez la couche de mappage, le SQL généré, la façon dont la pièce se communique, etc. Dans le cas d'une base de données coûteuse, vous vous assurez également de ne pas '' t besoin d'acheter une licence pour tout le monde. Vous pouvez détecter plus d'erreurs couvrant 90% du code avec des tests exécutés une fois par heure que couvrant 100% des tests de code une fois par jour ou pire, une semaine.
Cela étant dit, vous devez bien sûr tester avec la vraie base de données et un environnement entièrement intégré. Vous ne pouvez pas exécuter ces tests aussi souvent, mais puisque votre test précédent vous a déjà donné confiance, il ne vous reste plus qu'un bug spécifique à la plate-forme.
la source
Pour faire des tests simples, se moquer de la couche d'accès à la base de données est parfaitement acceptable. Vous appelez
getName()
, il appelle le DAO qui a été moqué et renvoie "John" pour le prénom et "Smith" pour le nom de famille, les assemble et tout est parfait. Pas besoin de tester réellement une base de données là-bas.Les choses deviennent un peu plus lorsque la logique devient un peu plus complexe. Et si vous aviez une méthode "createOrUpdateUser (...)". Si vous vous êtes moqué de la base de données, vous pouvez vérifier qu'une méthode donnée a été appelée une fois avec un certain paramètre lorsque la maquette ne renvoie aucun objet et qu'une méthode différente est invoquée sur la base de données lorsqu'elle renvoie un objet existant. Cela commence à atteindre cette ligne floue où il pourrait être plus facile (surtout si elle était déjà là) de créer une base de données mémoire spécialisée et de tester ce code avec des données préconfigurées.
Dans un code réel sur lequel j'ai travaillé (point de vente), nous avions une
resumeSuspededTransaction(...)
méthode. Cela tirerait la transaction de la base de données vers un objet (et ses composants) et mettrait à jour la base de données. Nous l'avions moqué et un bug se cachait quelque part dans le code avec la sérialisation et la désérialisation des données allant à la base de données (nous avons changé un type qui était sérialisé différemment dans la base de données).La maquette ne nous a pas montré le bogue car elle retournait son chemin heureux - sérialiser la transaction, la stocker dans la maquette, la désérialiser à partir de la simulation, tester qu'elles sont égales. Toutefois, lorsque vous sérialisez un objet avec un zéro non significatif dans la base de données, il les supprimait puis le recombinait en une chaîne sans les zéros. Nous avons détecté le bogue sans la base de données lors du dépannage (ce n'était pas si difficile à repérer une fois que nous savions qu'il était là).
Plus tard, nous avons mis une base de données là-dedans et nous avons réalisé que le bogue n'aurait jamais traversé ce test junit si nous allions à la place dans une base de données en mémoire.
Les bases de données en mémoire ont les avantages:
la source
Cela dépend beaucoup du système de base de données que vous utilisez. Lorsque votre système de base de données vous fournit une alternative en mémoire qui est presque 100% API et un comportement compatible avec une configuration de base de données sur disque (à l'exception de la vitesse et de la sécurité intrinsèque, ou bien sûr), alors l'utilisation de la variante en mémoire est évidemment très bien .
Si, cependant, votre système de base de données présente des différences importantes entre la configuration en mémoire et l'utilisation non en mémoire, vous avez raison: dans ce cas, les tests d'intégration ont un risque plus élevé de masquer un bogue. Mais même alors, vous pourrez peut-être "résumer ces différences" par vous-même, étant donné que vous connaissez bien votre système de base de données et les différences.
la source
Dans les mots du profane:
La moquerie de parties importantes de l'architecture est OK (et indispensable) pour les tests unitaires .
Mais pour les tests d'intégration, je suis entièrement d'accord avec vous. Il ne faut pas se moquer et un environnement aussi similaire que possible au réel doit être fourni.
Après tout, les tests d'intégration consistent à tester le comportement des différentes parties de l'architecture.
la source