Comment puis-je utiliser des tests unitaires et TDD pour tester une application qui repose principalement sur les opérations CRUD de base de données?

22

Au travail, l'un de mes projets consiste principalement à prendre des données transmises par un client externe et à les conserver dans une base de données. Il s'agit d'une application d'entreprise Java utilisant JPA et la plupart de nos logiques tournent autour des opérations CRUD.

La majorité de nos bogues impliquent JPA d'une manière ou d'une autre.

  • Exemple 1: Si vous cliquez deux fois sur le bouton Enregistrer, JPA peut essayer d'insérer la même entité dans la base de données une deuxième fois, provoquant une violation de clé primaire.
  • Exemple 2: vous récupérez une entité de la base de données, la modifiez et essayez de mettre à jour ses données. JPA peut essayer de créer une nouvelle instance au lieu de mettre à jour l'ancienne.

Souvent, la solution doit ajouter / supprimer / modifier une annotation JPA. D'autres fois, il s'agit de modifier la logique DAO.

Je ne peux pas comprendre comment obtenir la confiance dans notre code en utilisant des tests unitaires et TDD. Je ne sais pas si c'est parce que les tests unitaires et TDD sont mal adaptés, ou si j'approche mal le problème.

Les tests unitaires semblent être un mauvais ajustement car je ne peux découvrir ces problèmes qu'au moment de l'exécution et je dois déployer sur un serveur d'applications pour reproduire les problèmes. Habituellement, la base de données doit être impliquée, ce que je considère être en dehors de la définition d'un test unitaire: ce sont des tests d'intégration.

TDD semble être un mauvais ajustement car la boucle de rétroaction de déploiement + test est si lente qu'elle me rend très improductif. La boucle de rétroaction de déploiement + test prend plus de 3 minutes, et c'est juste si j'exécute les tests spécifiquement sur le code que j'écris. Pour exécuter tous les tests d'intégration prend plus de 30 minutes.

Il y a du code en dehors de ce moule et je teste toujours cela chaque fois que je le peux. Mais la majorité de nos bogues et les plus gros puits de temps impliquent toujours JPA ou la base de données.


Il y a une autre question qui est similaire , mais si je suivais les conseils, j'envelopperais la partie la plus instable de mon code (le JPA) et testerais tout sauf lui. Dans le cadre de ma question, je serais dans la même mauvaise situation. Quelle est la prochaine étape après avoir enveloppé l'APP? OMI, cette question est (peut-être) une étape pour répondre à ma question, mais pas une réponse.

Daniel Kaplan
la source
4
Ce que vous faites est essentiellement un test d'intégration, car vous devez configurer la base de données pour tester réellement. Je peux imaginer qu'un module dépendrait des autres, alors faites-le encore plus comme un test d'intégration. Je changerais la question que vous avez sur la façon d'appliquer les approches TDD à votre application.
Informé le
@randomA correct, j'ai modifié ma question pour le dire explicitement. Je ne comprends pas pourquoi vous recommandez de changer la question. Peux-tu élaborer? Je veux garder la partie de test unitaire là - bas parce que je plutôt être en train d' écrire des tests unitaires que les tests d'intégration (même si je suis conscient du fait que unit testing != TDD)
Daniel Kaplan
rien de spécial cependant, il suffit de mettre TDD là. Si vous avez un test unitaire là-bas, alors beaucoup de gens penseraient que vous ne comprenez rien, etc. pas bon pour vous ..
InformedA

Réponses:

7

Une option consiste à utiliser une base de données de test en mémoire telle que H2 ; il a tendance à être environ 10 fois plus rapide qu'une base de données utilisant un disque standard, et avec des temps de démarrage / démontage inférieurs.

Que cela aide ou non dépend en grande partie du fait que les problèmes JPA que vous rencontrez sont suffisamment généraux pour qu'ils échouent toujours sur différentes bases de données. Pas beaucoup de tests en cours d'exécution plus rapides s'ils ratent la majeure partie des problèmes.

Mais si vous pouvez faire 10 courses avec H2 pour chacun avec le système complet, cela pourrait être payant.

Soru
la source
C'est une bonne idée, mais je dois encore déployer sur le serveur d'application, AFAIK. Cela fait beaucoup des 3+ minutes. Cela dit, cela vaut vraiment la peine. Mais il est toujours difficile d'imaginer exécuter les tests aussi souvent que j'exécuterais des tests unitaires et il semble donc inefficace de développer en utilisant TDD.
Daniel Kaplan
1
Je pense qu'il existe généralement des moyens de contourner cette exigence (par exemple docs.oracle.com/middleware/1212/toplink/TLADG/testingjpa.htm ). Assez grande chance d'être plus de travail que justifié cependant; une autre option serait d'obtenir des serveurs de test plus puissants et d'exécuter les choses en parallèle.
soru
1
@tieTYT Ma propre preuve de concept avec l'unité hsqldb testant une application web crud sur github: TestingWithHsqldb - les tests unitaires n'ont pas besoin de déployer l'application.
3

Les bases de données peuvent être très faciles à tester unitairement - vous avez besoin de procédures et de transactions stockées.

Voici ce que dit Microsoft à propos des tests unitaires de base de données . Vous pouvez également exécuter des tests unitaires sur une base de données, écrire vos tests en Java ou en C # en configurant une connexion DB, en commençant une transaction, en écrivant toutes les données que vous souhaitez utiliser pour le test dans la base de données, en exécutant les tests et en les annulant. Aucun dommage à la base de données si vous en utilisiez une que vous avez également déployée et que vous obtenez des tests entièrement isolés.

J'espère que cela peut vous donner un aperçu de la façon de le faire dans votre cadre.

gbjbaanb
la source
Comme je l'ai dit, "La majorité de nos bugs impliquent JPA d'une manière ou d'une autre.", Je pense que les conseils de l'article manqueraient à tous. De plus, si vous considérez que ces tests Java / C # sont toujours des tests unitaires, nous avons des définitions très différentes. Je pense que c'est un bon conseil en général, mais il semble toujours que cela prendrait une tonne de temps pour déployer et exécuter la suite et donc pas propice à TDD. Êtes-vous en désaccord?
Daniel Kaplan
Nous avions l'habitude d'exécuter des tests unitaires DB pour notre SQL, mais ils étaient tous dans des sprocs. Bien que vous puissiez tester le répertoire sql unitaire à partir d'autres procédures sql, notre framework de test unitaire était MSTest, il était donc logique de les exécuter à partir de là (hé, nous avons reçu des graduations vertes dans le serveur de construction, ce qui était le facteur le plus important). Si vous avez toujours une base de données (et nous l'avons fait pour les tests internationaux de toute façon), il est facile de télécharger tout le code SQL et d'exécuter tous les tests unitaires sur le serveur de génération. Parfois, il suffit d'être pragmatique à propos de ces choses.
gbjbaanb
Je ne pense pas que vous ayez répondu à ma première phrase.
Daniel Kaplan
eh bien, utilisez alors jpa-unit. Je ne peux pas répondre au fonctionnement (ou non) de votre code JPA, essayez simplement de vous donner quelques idées pour faire tester ce sql dans la base de données.
gbjbaanb
3

D'autres personnes ont répondu "Mock out your DB!" - mais quel est l'intérêt de se moquer de votre couche DB si vous avez réellement besoin de tester comment il interagit avec votre code?

Ce que vous recherchez, ce sont des tests d'intégration et / ou des tests d'interface utilisateur automatisés. Vous avez mentionné que le problème se produit lorsque:

*If you click the save button twice*

La seule façon de tester cela est d'écrire un test d'interface utilisateur automatisé pour cliquer deux fois sur le bouton. Peut-être consultez le sélénium.

Vous aurez probablement aussi besoin d'une base de données de tests unitaires et pour vos tests, dirigez-la vers cela. Une douleur à maintenir mais bienvenue au TDD dans le monde réel.

Rocklan
la source
cela ressemble plus à une diatribe qu'à une réponse
moucher
J'ai répondu à la question à trois reprises - tests d'intégration, tests GUI et / ou une base de données de tests unitaires. Oui, c'est un peu une diatribe, je vais le modifier pour un semblant de raison maintenant.
Rocklan
1
"La seule façon de tester cela est d'écrire un test d'interface utilisateur automatisé pour cliquer deux fois sur le bouton. Peut-être consultez Selenium." Dans de telles situations, le backend devrait mieux empêcher que cela se produise, sinon l'interface utilisateur aurait un accès direct à la base de données.
Daniel Kaplan
0

Dans l'exemple que vous donnez dans votre question, vous ne pouvez pas effectuer de test unitaire / TDD dans la situation de cliquer deux fois sur le bouton pour provoquer une erreur très facilement. Mais ce que vous pouvez tester unitairement, c'est que dans le code qui est appelé lorsque vous cliquez sur le bouton, si vous obtenez une exception de la couche de persistance, vous la gérez de manière appropriée (soit en simulant la couche de persistance, soit en utilisant une base de données en mémoire comme a été suggéré dans d'autres réponses) - soit en relançant ou en affichant une erreur ou autre chose.

Vous avez raison de dire que TDD peut commencer à tomber en panne lorsque vous devez effectuer des tests qui ne conviennent pas à un test unitaire (c.-à-d. Tests d'intégration / système) - cela a constitué une grande partie de la discussion dans le récent "Is TDD Mort?" débats entre Kent Beck, Martin Fowler et David Heinemeier Hansson: http://martinfowler.com/articles/is-tdd-dead/

Chris Cooper
la source