Les tests d'intégration (de base de données) sont-ils mauvais?

120

Certaines personnes soutiennent que les tests d'intégration sont des types de tests incorrects et erronés - tout doit être testé en unité, ce qui signifie que vous devez simuler des dépendances. une option qui, pour diverses raisons, ne me passionne pas toujours.

Je trouve que, dans certains cas, un test unitaire ne prouve simplement rien.

Prenons l'exemple de l'implémentation de référentiel (trivial, naïf) suivant (en PHP):

class ProductRepository
{
    private $db;

    public function __construct(ConnectionInterface $db) {
        $this->db = $db;
    }

    public function findByKeyword($keyword) {
        // this might have a query builder, keyword processing, etc. - this is
        // a totally naive example just to illustrate the DB dependency, mkay?

        return $this->db->fetch("SELECT * FROM products p"
            . " WHERE p.name LIKE :keyword", ['keyword' => $keyword]);
    }
}

Supposons que je souhaite prouver par un test que ce référentiel peut réellement trouver des produits correspondant à différents mots clés donnés.

En dehors des tests d'intégration avec un objet de connexion réel, comment puis-je savoir que cela génère en fait de vraies requêtes - et que ces requêtes font réellement ce que je pense qu'elles font?

Si je dois simuler l'objet de connexion dans un test unitaire, je ne peux que prouver que "il génère la requête attendue" - mais cela ne signifie pas qu'il va réellement fonctionner ... c'est-à-dire qu'il génère peut-être la requête. Je m'y attendais, mais peut-être que cette requête ne fait pas ce que je pense.

En d'autres termes, j'ai l'impression qu'un test qui émet des assertions sur la requête générée est essentiellement sans valeur, car il teste la manière dont la findByKeyword()méthode a été implémentée , mais cela ne prouve pas que cela fonctionne réellement .

Ce problème ne se limite pas aux référentiels ou à l’intégration de bases de données - il semble s’appliquer dans de nombreux cas, où affirmer l’utilisation d’un simulacre de test (double test) ne fait que prouver comment les choses sont mises en œuvre, et non pas si elles vont être utilisées. fonctionne réellement.

Comment gérez-vous de telles situations?

Les tests d'intégration sont-ils vraiment "mauvais" dans un cas comme celui-ci?

Je comprends qu'il est préférable de tester une chose, et je comprends aussi pourquoi les tests d'intégration mènent à une myriade de chemins de code, qui ne peuvent pas tous être testés - mais dans le cas d'un service (tel qu'un référentiel) dont le seul but est Pour interagir avec un autre composant, comment pouvez-vous réellement tester quoi que ce soit sans tester l'intégration?

mindplay.dk
la source
5
Lisez agitar.com/downloads/TheWayOfTestivus.pdf , en particulier la page 6 "Le test est plus important que l'unité".
Doc Brown
2
@ user61852 il est dit "naïf" dans la description, oui?
mindplay.dk
4
Comment votre collègue serait-il absolument certain que sa base de données simulée se comporte comme une réalité?
Thorbjørn Ravn Andersen
12
Vous essayez d'être réaliste. Votre collègue essaie de respecter les règles. Toujours écrire des tests qui produisent de la valeur . Ne perdez pas de temps à écrire des tests impossibles à maintenir, et n'écrivez pas de tests n'effectuant aucune des actions suivantes: augmentez la probabilité que votre code soit correct ou vous oblige à écrire un code plus facilement maintenable.
jpmc26
3
@ mindplay.dk: la phrase clé de ce paragraphe est "Mais ne reste pas bloqué sur un dogme. Écris le test qui doit être écrit." Votre collègue semble être coincé dans un dogme. Et vous n'avez pas besoin de quelqu'un vous expliquer ce que le test doit être écrit dans votre exemple - vous le savez déjà. Il est assez évident que pour tester si votre base de données comprend la requête, vous devez exécuter la requête sur la base de données réelle - aucune simulation ne peut vous dire ceci
Doc Brown

Réponses:

129

Votre collègue a raison de dire que tout ce qui peut être testé en unité devrait l'être et que les tests unitaires ne vous mèneront que très loin, en particulier lorsque vous écrivez de simples wrappers autour de services externes complexes.

Une façon courante de penser aux tests consiste à utiliser une pyramide de tests . C'est un concept fréquemment associé à Agile, et beaucoup l'ont écrit, y compris Martin Fowler (qui l'attribue à Mike Cohn dans Succeeding with Agile ), Alistair Scott et le blog Google Testing .

        /\                           --------------
       /  \        UI / End-to-End    \          /
      /----\                           \--------/
     /      \     Integration/System    \      /
    /--------\                           \----/
   /          \          Unit             \  /
  --------------                           \/
  Pyramid (good)                   Ice cream cone (bad)

L'idée est que des tests unitaires rapides et résilients constituent le fondement du processus de test: il devrait exister davantage de tests unitaires ciblés que de tests système / d'intégration et davantage de tests système / d'intégration que de tests de bout en bout. Au fur et à mesure que vous vous rapprochez du sommet, les tests prennent plus de temps / ressources à exécuter, ont tendance à être plus fragiles et plus fragiles, et sont moins spécifiques dans l'identification du système ou du fichier cassé ; naturellement, il est préférable d'éviter d'être trop lourd.

À ce stade, les tests d'intégration ne sont pas mauvais , mais une forte confiance en eux peut indiquer que vous n'avez pas conçu vos composants individuels pour qu'ils soient faciles à tester. Rappelez-vous que le but ici est de vérifier que votre appareil respecte ses spécifications tout en utilisant au minimum d’autres systèmes fragiles : Vous pouvez essayer une base de données en mémoire (que je considère comme un test à l’unité permettant de tester facilement les unités) ) pour des tests de cas extrêmes, par exemple, puis écrivez quelques tests d'intégration avec le moteur de base de données réel pour établir que les cas principaux fonctionnent lors de l'assemblage du système.


En remarque, vous avez mentionné que les simulacres que vous écrivez testent simplement comment quelque chose est implémenté, pas si cela fonctionne . C'est un peu un anti-modèle: un test qui reflète parfaitement son implémentation ne teste pas vraiment quoi que ce soit. Au lieu de cela, vérifiez que chaque classe ou méthode se comporte conformément à ses propres spécifications , quel que soit le niveau d'abstraction ou de réalisme requis.

Jeff Bowman
la source
13
+1 pour "Un test qui reflète parfaitement son implémentation ne teste rien du tout." Trop commun. J'appelle cela l' antipattern de Doppelganger .
dodgethesteamroller
6
Une école contrarian d’assurance qualité logicielle, le mouvement de test basé sur le contexte , est en partie vouée à contester l’existence de règles empiriques aussi utiles que la pyramide de test. En particulier, les textes fondateurs du mouvement donnent de nombreux exemples dans lesquels les tests d'intégration sont beaucoup plus utiles que d'autres types de tests (car ils testent le système dans son contexte, en tant que système ) ....
dodgethesteamroller
7
... par conséquent, Fowler et al., faisant valoir que les tests d'intégration et les tests d'acceptation des utilisateurs doivent faire l'objet de moins d'efforts, car ils sont trop difficiles à écrire de manière robuste et maintenable, ne fournissent en réalité qu'une excuse ex post facto pour expliquer pourquoi ils n'ont pas trouvé comment bien tester à des niveaux plus élevés.
dodgethesteamroller
1
@dodgethesteamroller Les discussions approfondies sur une "école à contre-courant" comme celle-ci conviennent probablement mieux à leur propre réponse. Personnellement, je trouve que le blog de test de Google décrit assez bien les vertus des tests automatisés rapides à portée étroite, parallèlement aux tests de système en contexte. Au cas où vous auriez trouvé cela incertain, j’énumère ici la pyramide de test comme un modèle ou un point de départ utile, et non comme une excuse pour arrêter de penser en tant qu’ingénieur.
Jeff Bowman
1
Présentation hautement recommandée sur la hiérarchie et le ratio des tests non testés sur les tests d'intégration: vimeo.com/80533536 Très bien expliqué.
Szalski
88

Un de mes collègues soutient que les tests d’intégration sont des sortes de mauvaises et de mauvaises méthodes - tout doit être testé en unité,

C'est un peu comme dire que les antibiotiques sont mauvais - tout devrait être soigné avec des vitamines.

Les tests unitaires ne peuvent pas tout comprendre: ils testent uniquement le fonctionnement d'un composant dans un environnement contrôlé . Les tests d'intégration vérifient que tout fonctionne ensemble , ce qui est plus difficile à faire mais plus significatif à la fin.

Un bon processus de test complet utilise les deux types de tests: des tests unitaires pour vérifier les règles métier et d'autres éléments pouvant être testés indépendamment, et des tests d'intégration pour s'assurer que tout fonctionne bien.

En dehors des tests d'intégration avec un objet de connexion réel, comment puis-je savoir que cela génère en fait de vraies requêtes - et que ces requêtes font réellement ce que je pense qu'elles font?

Vous pouvez le tester à l' unité au niveau de la base de données . Exécutez la requête avec divers paramètres et voyez si vous obtenez les résultats escomptés. Accordé signifie copier / coller toute modification dans le "vrai" code. mais il ne vous permet de tester la requête indépendante de toutes les autres dépendances.

D Stanley
la source
Ne seriez-vous pas en train de tester si la base de données contient ou non certaines données?
Tulains Córdova
Peut-être, mais vous pouvez également tester le fonctionnement de vos filtres, jointures complexes, etc. L'exemple de requête n'est probablement pas le meilleur candidat pour les "tests unitaires", mais peut-être avec des jointures complexes et / ou des agrégations.
D Stanley
Oui, l’exemple que j’ai utilisé, comme indiqué, est trivial; un vrai référentiel pourrait avoir toutes sortes d'options de recherche et de tri complexes, par exemple en utilisant un constructeur de requêtes, etc.
mindplay.dk
2
Bonne réponse, mais j'ajouterais que la base de données doit être en mémoire, afin de s'assurer que les tests unitaires sont rapides.
Octobre
3
@ BЈовић: Malheureusement, cela n'est peut-être pas toujours possible, car il n'existe malheureusement pas deux bases de données compatibles, qui ne fonctionnent pas toutes en mémoire. Il existe également des problèmes de licence pour les bases de données commerciales (vous n’avez peut-être pas la licence pour l’exécuter sur aucune machine), ...
Matthieu M.
17

Les tests unitaires ne détectent pas tous les défauts. Mais ils sont moins chers à installer et à (ré) exécuter par rapport à d’autres types de tests. Les tests unitaires sont justifiés par une combinaison de valeur modérée et de coût faible à modéré.

Voici un tableau indiquant les taux de détection des défauts pour différents types de tests.

entrez la description de l'image ici

source: p.470 dans Code Complete 2 par McConnell

Nick Alexeev
la source
5
Ces données ont été recueillies en 1986 . C'était il y a trente ans . Les tests unitaires de 1986 n'étaient pas ce qu'ils sont aujourd'hui. Je serais sceptique quant à ces données. Sans oublier que les tests unitaires détectent le bogue avant qu'il ne soit commis , il est donc douteux qu'ils soient signalés.
RubberDuck
3
@RubberDuck Ce graphique provient d'un livre de 2006 et est basé sur des données de 1986, 1996 et 2002 (si vous regardez attentivement). Je n'ai pas examiné les protocoles de collecte de données dans les sources, je ne peux pas dire quand elles ont commencé à suivre un défaut et comment il a été signalé. Ce tableau pourrait-il être un peu dépassé? Ça pourrait. En décembre dernier, j'étais à un séminaire et l'instructeur a mentionné que les tests d'intégration détectent plus de bogues que les tests unitaires (par un facteur de 2, iirc). Ce tableau indique qu'ils sont à peu près les mêmes.
Nick Alexeev
13

Non, ils ne sont pas mauvais. Espérons que l'on devrait avoir des tests unitaires et d'intégration. Ils sont utilisés et exécutés à différentes étapes du cycle de développement.

Tests unitaires

Les tests unitaires doivent être exécutés sur le serveur de génération et localement, une fois le code compilé. Si des tests unitaires échouent, il faut échouer lors de la construction ou ne pas valider la mise à jour du code jusqu'à ce que les tests soient corrigés. La raison pour laquelle nous voulons que les tests unitaires soient isolés est que nous souhaitons que le serveur de build puisse exécuter tous les tests sans toutes les dépendances. Ensuite, nous pourrions exécuter la construction sans toutes les dépendances complexes requises et effectuer un grand nombre de tests très rapides.

Donc, pour une base de données, on devrait avoir quelque chose comme:

IRespository

List<Product> GetProducts<String Size, String Color);

Maintenant, l'implémentation réelle d'IRepository ira à la base de données pour obtenir les produits, mais pour les tests unitaires, on peut simuler IRepository avec un faux pour exécuter tous les tests nécessaires sans base de données actaul, car nous pouvons simuler toutes sortes de listes de produits. renvoyé à partir de l'instance fictive et testez toute logique métier avec les données fictives.

Tests d'intégration

Les tests d'intégration sont généralement des tests de franchissement de limites. Nous voulons exécuter ces tests sur le serveur de déploiement (environnement réel), le bac à sable ou même localement (pointé sur le bac à sable). Ils ne sont pas exécutés sur le serveur de compilation. Une fois que le logiciel a été déployé dans l'environnement, ceux-ci sont généralement exécutés comme une activité post-déploiement. Ils peuvent être automatisés via des utilitaires de ligne de commande. Par exemple, nous pouvons exécuter nUnit à partir de la ligne de commande si nous catégorisons tous les tests d'intégration que nous souhaitons appeler. Celles-ci appellent en réalité le référentiel réel avec l'appel de la base de données réelle. Ce type de test aide à:

  • Environnement Santé Stabilité Préparation
  • Tester la vraie chose

Ces tests sont parfois plus difficiles à exécuter car nous pouvons avoir besoin de les installer et / ou de les démolir également. Pensez à ajouter un produit. Nous souhaitons probablement ajouter le produit, l'interroger pour savoir s'il a été ajouté, puis le supprimer une fois l'opération terminée. Nous ne souhaitons pas ajouter 100 ou 1 000 produits "d'intégration", une configuration supplémentaire est donc nécessaire.

Les tests d'intégration peuvent s'avérer très utiles pour valider un environnement et s'assurer que la réalité fonctionne.

On devrait avoir les deux.

  • Exécutez les tests unitaires pour chaque build.
  • Exécutez les tests d'intégration pour chaque déploiement.
Jon Raynor
la source
Je recommanderais d'exécuter des tests d'intégration pour chaque build, plutôt que de devoir s'engager et pousser. Cela dépend du temps qu’ils prennent, mais c’est aussi une bonne idée de les garder rapidement pour de nombreuses raisons.
artbristol
@ArtBristol - En règle générale, la dépendance de l'environnement n'est pas configurée sur notre serveur de génération. Par conséquent, nous ne pouvons pas exécuter nos tests d'intégration à cet emplacement. Mais si l’on peut y exécuter les tests d’intégration, allez-y. Nous avons un sandbox de déploiement configuré après la construction que nous utilisons pour les tests d'intégration afin de vérifier le déploiement. Mais chaque situation est différente.
Jon Raynor
11

Les tests d'intégration de base de données ne sont pas mauvais. Encore plus, ils sont nécessaires.

Votre application est probablement divisée en couches, et c'est une bonne chose. Vous pouvez tester chaque couche séparément en vous moquant des couches voisines, ce qui est également une bonne chose. Mais quel que soit le nombre de couches d'abstraction que vous créez, il doit y avoir à un moment donné une couche qui fait le sale boulot - parler en fait à la base de données. À moins de le tester, vous ne le testez pas du tout. Si vous testez la couche n en vous moquant de la couche n-1, vous évaluez l'hypothèse selon laquelle la couche n fonctionne à condition que la couche n-1 fonctionne. Pour que cela fonctionne, vous devez prouver que la couche 0 fonctionne.

Tandis qu'en théorie, vous pourriez utiliser la base de données de test unitaire, en analysant et en interprétant le SQL généré, il est beaucoup plus facile et plus fiable de créer une base de test à la volée et d’en parler.

Conclusion

Quelle est la confiance garantie par les tests unitaires des couches référentiel abstrait , mappeur d'objet relationnel Ethereal , enregistrement actif générique , persistance théorique , lorsque, finalement, votre code SQL généré contient une erreur de syntaxe?

el.pescado
la source
Je pensais ajouter une réponse semblable à la vôtre mais vous l'avez mieux dite! D'après mon expérience, effectuer des tests sur la couche qui récupère et stocke les données m'a évité beaucoup de peine.
Daniel Hollinrake
Les tests d'intégration de base de données sont mauvais. Avez-vous une base de données disponible dans votre ligne ci / cd? Cela me semble assez complexe. Il est beaucoup plus facile de se moquer de la base de données et de construire une couche d'abstraction pour l'utiliser. C'est non seulement une solution beaucoup plus élégante, mais aussi rapide que possible. Les tests unitaires doivent être rapides. Tester votre base de données ralentit considérablement vos testeurs à un niveau inacceptable. Unittests ne devrait pas prendre plus de 10 minutes, même lorsque vous en avez des milliers.
David
@ David Avez-vous une base de données disponible dans votre ligne ci / cd? Bien sûr, c'est assez classique caractéristique . BTW, je ne préconise pas les tests d'intégration au lieu des tests unitaires - je préconise des tests d'intégration en conjonction avec des tests unitaires. Les tests unitaires rapides sont essentiels, mais les bases de données sont trop complexes pour reposer sur des tests unitaires avec des interactions simulées.
el.pescado
@ el.pescado je dois être en désaccord. Si votre communication de base de données est derrière une couche d'abstraction, il est vraiment facile de se moquer. Vous pouvez simplement décider quel objet à retourner. En outre, le fait que quelque chose soit la norme n'en fait pas une bonne chose.
David
@ David Je pense que cela dépend de votre approche de la base de données. Est-ce le détail de la mise en œuvre ou une partie vitale du système ? (Je me penche vers ce dernier) . Si vous traitez la base de données comme un stockage de données stupide, alors oui, vous pourriez le faire sans tests d'intégration. Cependant, s'il existe une logique dans la base de données - contraintes, déclencheurs, clés étrangères, transactions ou si votre couche de données utilise un code SQL personnalisé au lieu de méthodes ORM simples, j'estime que les tests unitaires ne seront pas suffisants.
el.pescado
6

Vous avez besoin des deux.

Dans votre exemple, si vous testiez une base de données dans certaines conditions, lorsque la findByKeywordméthode est exécutée, vous récupérez les données qui devraient, selon vous, constituer un test d'intégration fin.

Dans tout autre code utilisant cette findByKeywordméthode, vous souhaitez contrôler le contenu du test. Vous pouvez ainsi renvoyer les valeurs NULL ou les mots corrects pour votre test, ou tout ce que vous ferez de la dépendance à la base de données, de sorte que vous sachiez exactement ce que sera votre test. recevez (et vous perdez la surcharge de vous connecter à une base de données et de vous assurer que les données sont correctes)

Froome
la source
6

L'auteur de l' article de blog auquel vous faites référence est principalement concerné par la complexité potentielle pouvant résulter de tests intégrés (même s'il est écrit de manière très catégorique et basée sur l'opinion). Cependant, les tests intégrés ne sont pas nécessairement mauvais et certains sont en réalité plus utiles que les tests unitaires purs. Cela dépend vraiment du contexte de votre application et de ce que vous essayez de tester.

De nos jours, de nombreuses applications ne fonctionneraient tout simplement pas du tout si leur serveur de base de données tombait en panne. Au moins, pensez-y dans le contexte de la fonctionnalité que vous essayez de tester.

D’une part, si ce que vous essayez de tester ne dépend pas ou ne peut en aucune manière dépendre de la base de données, écrivez votre test de telle sorte qu’il n’essaye même pas d’utiliser la base de données (juste fournir des données factices si nécessaire). Par exemple, si vous essayez de tester une logique d’authentification lorsque vous servez une page Web (par exemple), il est probablement préférable de la dissocier de la base de données (en supposant que vous ne vous fiez pas à la base de données pour l’authentification. vous pouvez vous moquer assez facilement).

D'autre part, s'il s'agit d'une fonctionnalité qui repose directement sur votre base de données et qui ne fonctionnerait pas du tout dans un environnement réel si la base de données était indisponible, alors vous vous moquez de ce que fait la base de données dans votre code client de base de données (c'est-à-dire DB) n'a pas nécessairement de sens.

Par exemple, si vous savez que votre application va s'appuyer sur une base de données (et éventuellement sur un système de base de données spécifique), se moquer du comportement de la base de données pour le plaisir sera souvent une perte de temps. Les moteurs de base de données (en particulier les SGBDR) sont des systèmes complexes. Quelques lignes de SQL peuvent en réalité effectuer beaucoup de travail, ce qui serait difficile à simuler (en fait, si votre requête SQL est longue, il est probable que vous aurez besoin de beaucoup plus de lignes de Java / PHP / C # / Python. code pour produire le même résultat en interne): dupliquer la logique que vous avez déjà implémentée dans la base de données n’a aucun sens, et vérifier que le code de test deviendrait alors un problème en soi.

Je ne considérerais pas nécessairement cela comme un problème de test unitaire par rapport à un test intégré , mais plutôt d'examiner l'étendue de ce qui est testé. Les problèmes généraux liés aux tests unitaires et d'intégration subsistent: vous avez besoin d'un ensemble raisonnablement réaliste de données de test et de scénarios de test, mais aussi d'une taille suffisamment petite pour que les tests puissent être exécutés rapidement.

Le temps nécessaire pour réinitialiser la base de données et repeupler avec les données de test est un aspect à prendre en compte; vous évalueriez généralement ceci par rapport au temps nécessaire pour écrire ce code factice (que vous auriez éventuellement à maintenir aussi).

Un autre point à considérer est le degré de dépendance de votre application avec la base de données.

  • Si votre application suit simplement un modèle CRUD, dans lequel vous avez une couche d’abstraction qui vous permet de basculer d’un SGBDR à l’aide d’un simple paramètre de configuration, il est fort probable que vous puissiez travailler avec un système fictif assez facilement la ligne entre l'unité et les tests intégrés à l'aide d'un SGBDR en mémoire).
  • Si votre application utilise une logique plus complexe, spécifique à l'un des serveurs SQL Server, MySQL et PostgreSQL (par exemple), il serait généralement plus judicieux de faire un test utilisant ce système spécifique.
Bruno
la source
"Aujourd'hui, de nombreuses applications ne fonctionneraient tout simplement pas du tout si leur serveur de base de données tombait en panne" - c'est un point très important!
el.pescado
Bonne explication des limites des simulacres complexes, comme utiliser un autre langage pour simuler SQL. Lorsque le code de test devient suffisamment compliqué pour donner l'impression qu'il doit être testé lui-même, c'est une odeur d'AQ.
dodgethesteamroller
1

Vous avez raison de penser qu'un tel test unitaire est incomplet. Le caractère incomplet se trouve dans l'interface de base de données en cours de simulation. L'attente ou les affirmations d'une telle maquette naïve sont incomplètes.

Pour le rendre complet, vous devez disposer de suffisamment de temps et de ressources pour écrire ou intégrer un moteur de règles SQL garantissant que les instructions SQL émises par le sujet testé entraînent les opérations attendues.

Cependant, l'alternative / compagne de moquerie souvent oubliée et un peu chère est la "virtualisation" .

Pouvez-vous lancer une instance de base de données temporaire, en mémoire, mais "réelle" pour tester une seule fonction? Oui ? Là, vous avez un meilleur test, celui qui vérifie les données réelles enregistrées et récupérées.

Maintenant, on pourrait dire que vous avez transformé un test unitaire en test d’intégration. Il existe différents points de vue sur les points sur lesquels tracer la ligne à classer entre les tests unitaires et les tests d'intégration. IMHO, "unité" est une définition arbitraire et devrait correspondre à vos besoins.

Dakota du Sud
la source
1
cela semble simplement répéter les points soulevés et expliqués dans cette réponse antérieure qui a été publiée il y a plusieurs heures
Gnat
0

Unit Testset Integration Testssont orthogonaux les uns aux autres. Ils offrent une vue différente de l'application que vous construisez. Habituellement, vous voulez les deux . Mais le moment diffère selon le type de test.

Le plus souvent tu veux Unit Tests. Les tests unitaires se concentrent sur une petite partie du code testé - ce qui est appelé exactement un unitest laissé au lecteur. Mais le but est simple: obtenir un retour rapide sur le moment et le lieu votre code a été cassé . Cela dit, il devrait être clair que les appels vers une base de données réelle est un nono .

D'autre part, il y a des choses qui ne peuvent être testées à l'unité que dans des conditions difficiles sans base de données. Votre code contient peut-être une condition de concurrence critique et un appel à une base de données renvoie une violation unique constraintqui ne peut être déclenchée que si vous utilisez réellement votre système. Mais ces types de tests sont coûteux, vous ne pouvez pas (et ne voulez pas) les exécuter aussi souvent que unit tests.

Thomas Junk
la source
0

Dans le monde .Net, j'ai l'habitude de créer un projet de test et de créer des tests en tant que méthode de codage / débogage / test aller-retour moins l'interface utilisateur. C'est un moyen efficace pour moi de me développer. Je n'étais pas aussi intéressé par l'exécution de tous les tests pour chaque version (car cela ralentit mon flux de travail de développement), mais je comprends son utilité pour une équipe plus grande. Néanmoins, vous pouvez faire savoir qu'avant de valider le code, tous les tests doivent être exécutés et passés (si l'exécution des tests est plus longue, car la base de données est en train d'être touchée).

Mocker la couche d'accès aux données (DAO) et ne pas frapper la base de données ne me permet pas non plus de coder comme je le souhaite et s'y est habitué, mais il manque un gros morceau de la base de code réelle. Si vous ne testez pas vraiment la couche d'accès aux données et la base de données et ne faites que simuler, puis passez beaucoup de temps à vous moquer, je ne parviens pas à saisir l'utilité de cette approche pour réellement tester mon code. Je teste un petit morceau au lieu d'un plus gros avec un test. Je comprends que mon approche ressemble peut-être davantage à un test d’intégration, mais il semble que le test unitaire avec la maquette est une perte de temps redondante si vous écrivez simplement le test d’intégration une fois pour la première fois. C'est aussi un bon moyen de développer et de déboguer.

En fait, je connais le TDD et la conception axée sur le comportement (BDD) et réfléchis depuis un moment à son utilisation, mais il est difficile d’ajouter des tests unitaires rétroactivement. Je me trompe peut-être, mais écrire un test qui couvre plus de code de bout en bout avec la base de données incluse semble être un test beaucoup plus complet et prioritaire à écrire qui couvre plus de code et constitue un moyen plus efficace d'écrire des tests.

En fait, je pense que quelque chose comme BDD (Behavior Driven Design) qui tente de tester de bout en bout un langage spécifique à un domaine (DSL) devrait être la solution. Nous avons SpecFlow dans le monde .Net, mais cela a commencé en open source avec Cucumber.

https://cucumber.io/

Je ne suis vraiment pas vraiment impressionné par la véritable utilité du test que j'ai écrit pour se moquer de la couche d'accès aux données et ne pas toucher à la base de données. L'objet renvoyé n'a pas touché la base de données et n'a pas été rempli de données. C'était un objet entièrement vide que je devais simuler de manière non naturelle. Je pense juste que c'est une perte de temps.

Selon Stack Overflow, le mocking est utilisé lorsqu'il est impossible d'incorporer des objets réels dans le test unitaire.

https://stackoverflow.com/questions/2665812/what-is-mocking

"Le mocking est principalement utilisé dans les tests unitaires. Un objet testé peut avoir des dépendances sur d'autres objets (complexes). Pour isoler le comportement de l'objet que vous souhaitez tester, remplacez les autres objets par des simulacres simulant le comportement des objets réels. Ceci est utile si les objets réels ne peuvent pas être incorporés au test unitaire. "

Mon argument est que, si je codifie quoi que ce soit bout à bout (interface utilisateur Web entre couche Web, couche d'accès aux données dans la base de données, aller-retour), avant d'enregistrer quoi que ce soit en tant que développeur, je vais tester ce flux aller-retour. Si je coupe l'interface utilisateur, que je débogue et teste ce flux à partir d'un test, je teste tout ce qui reste avant l'interface utilisateur et renvoie exactement ce à quoi l'interface utilisateur s'attend. Il ne me reste plus qu'à envoyer à l'interface utilisateur ce qu'elle veut.

J'ai un test plus complet qui fait partie de mon flux de travail de développement naturel. Pour moi, il devrait s'agir du test de priorité la plus élevée couvrant le test de bout en bout de la spécification de l'utilisateur. Si je ne crée jamais d'autres tests plus granulaires, au moins j'ai ce test plus complet qui prouve que la fonctionnalité souhaitée fonctionne.

Un cofondateur de Stack Exchange n'est pas convaincu des avantages d'une couverture de test unitaire à 100%. Moi aussi je ne suis pas. Je prendrais un "test d'intégration" plus complet qui frappe la base de données en conservant un tas de bases de données mock tous les jours.

https://www.joelonsoftware.com/2009/01/31/from-podcast-38/

utilisateur3198764
la source
il est tellement évident que vous ne comprenez pas la différence entre les tests unitaires et les tests d'intégration
BЈовић 10/03/17
Je pense que cela dépend du projet. Sur des projets plus petits avec moins de ressources où un développeur est plus globalement responsable des tests et des tests de régression en raison du manque de testeurs et du maintien de la synchronisation de la documentation avec le code, si je passe du temps à écrire des tests, ce seront ceux qui me donne le plus pour mon argent. Je veux tuer autant d'oiseaux avec une pierre que possible. Si la plupart de mes problèmes logiques et de mes problèmes proviennent de procédures stockées dans la base de données qui génèrent des rapports ou de JavaScript, la couverture complète des tests unitaires dans la couche intermédiaire n'aide pas beaucoup.
user3198764
-1

Les dépendances externes doivent être simulées car vous ne pouvez pas les contrôler (elles peuvent réussir lors de la phase de test d'intégration mais échouer en production). Les lecteurs peuvent échouer, les connexions à la base de données peuvent échouer pour un certain nombre de raisons, il peut y avoir des problèmes de réseau, etc.

Avec de vrais tests unitaires, vous testez dans les limites du bac à sable et cela devrait être clair. Si un développeur écrit une requête SQL qui a échoué dans QA / PROD, cela signifie qu'il ne l'a même pas testée une seule fois auparavant.

vidalsasoon
la source
+1 pour "vous ne pouvez pas les contrôler (ils peuvent passer lors de la phase de test d'intégration mais échouer en production)" .
Tulains Córdova
Vous pouvez les contrôler au degré de satisfaction.
el.pescado
Je comprends ce que vous voulez dire, mais je pense que cela était plus vrai qu'aujourd'hui? Avec l’automatisation et des outils (comme Docker), vous pouvez réellement répliquer et répéter la configuration de toutes vos dépendances binaires / serveur de manière précise et fiable pour les suites de tests d’intégration. Bien sûr, oui, le matériel physique (et les services tiers, etc.) peut échouer.
mindplay.dk
5
Je ne suis absolument pas d'accord. Vous devez écrire des tests d'intégration (supplémentaires) car les dépendances externes peuvent échouer. Les dépendances externes peuvent avoir leurs propres bizarreries, ce qui vous manquera probablement lorsque vous vous moquez de tout.
Paul Kertscher
1
@PaulK réfléchit toujours à la réponse à marquer comme acceptée, mais je penche vers la même conclusion.
mindplay.dk