(Pourquoi) est-il important qu'un test unitaire ne teste pas les dépendances?


103

Je comprends la valeur des tests automatisés et l’utilise chaque fois que le problème est suffisamment spécifié pour que je puisse proposer de bons scénarios de tests. J'ai toutefois remarqué que certaines personnes ici et sur StackOverflow insistent sur le fait de tester uniquement une unité, pas ses dépendances. Ici, je ne vois pas le bénéfice.

Le mocking / stubbing pour éviter de tester les dépendances ajoute de la complexité aux tests. Il ajoute des exigences de flexibilité / découplage artificielles à votre code de production pour prendre en charge les moqueries. (Je suis en désaccord avec quiconque dit que cela favorise une bonne conception. Écrire du code supplémentaire, introduire des choses telles que des frameworks d'injection de dépendance, ou ajouter de la complexité à votre base de code pour rendre les choses plus flexibles / plugables / extensibles / découplés sans réel cas d'utilisation, c'est trop d'ingénierie, bon design.)

Deuxièmement, tester les dépendances signifie que le code de bas niveau critique utilisé partout est testé avec des entrées autres que celles auxquelles l'auteur des tests a explicitement pensé. J'ai trouvé de nombreux bogues dans les fonctionnalités de bas niveau en effectuant des tests unitaires sur les fonctionnalités de haut niveau sans modifier la fonctionnalité de bas niveau dont elle dépendait. Idéalement, ces tests auraient été trouvés par les tests unitaires pour la fonctionnalité de bas niveau, mais les cas manqués se produisent toujours.

Quel est le côté opposé à cela? Est-il vraiment important qu'un test unitaire ne teste pas également ses dépendances? Si oui, pourquoi?

Edit: Je peux comprendre l’intérêt de moquer des dépendances externes telles que des bases de données, des réseaux, des services Web, etc. (Merci à Anna Lear de me motiver à clarifier cela.) Je parlais de dépendances internes , c’est-à-dire d’autres classes, de fonctions statiques, etc. qui n'ont pas de dépendances externes directes.


14
"ingénierie excessive, pas bonne conception". Vous allez devoir fournir plus de preuves que cela. Beaucoup de gens ne l'appelleraient pas «plus d'ingénierie», mais «meilleure pratique».
S.Lott

9
@ S.Lott: Bien sûr, c'est subjectif. Je ne voulais tout simplement pas que toutes les réponses à ce problème soient résolues et que le fait de se moquer soit une bonne chose, car rendre votre code moqueur favorise un bon design. En général, cependant, je déteste traiter avec un code découplé d’une manière qui n’a aucun avantage évident maintenant ou dans un avenir prévisible. Si vous n'avez pas plusieurs implémentations maintenant et ne prévoyez pas de les avoir dans un avenir proche, alors IMHO vous devriez simplement le coder en dur. C'est plus simple et n'ennuie pas le client avec les détails des dépendances d'un objet.
dsimcha

5
En général, cependant, je déteste traiter avec du code qui est couplé d'une manière qui n'a pas de raison évidente, à l'exception d'une mauvaise conception. C'est plus simple et n'ennuie pas le client avec la possibilité de tester les choses isolément.
S.Lott

3
@ S.Lott: Clarification: je voulais dire un code qui décode de manière significative les choses sans aucun cas d'utilisation clair, en particulier lorsqu'il rend le code ou son code client beaucoup plus détaillé, introduit une autre classe / interface, etc. Bien sûr, je ne demande pas que le code soit plus étroitement couplé que la conception la plus simple et la plus concise. En outre, lorsque vous créez prématurément des lignes d’abstraction, elles se retrouvent généralement au mauvais endroit.
dsimcha

Pensez à mettre à jour votre question pour clarifier la distinction que vous faites. Intégrer une séquence de commentaires est difficile. Veuillez mettre à jour la question pour la clarifier et la cibler.
S.Lott

Réponses:


116

C'est une question de définition. Un test avec des dépendances est un test d'intégration, pas un test unitaire. Vous devriez également avoir une suite de tests d'intégration. La différence est que la suite de tests d'intégration peut être exécutée dans une autre structure de test et probablement pas dans le cadre de la construction, car elle prend plus de temps.

Pour notre produit: Nos tests unitaires sont exécutés à chaque construction, en quelques secondes. Un sous-ensemble de nos tests d'intégration s'exécute à chaque enregistrement, prenant 10 minutes. Notre suite d'intégration complète est exécutée chaque nuit et prend 4 heures.


4
N'oubliez pas les tests de régression qui couvrent les bogues découverts et corrigés afin d'empêcher leur réintroduction dans un système lors d'une maintenance future.
RBerteig

2
Je n'insisterais pas sur la définition même si je l'acceptais. Certains codes peuvent avoir des dépendances acceptables comme un StringFormatter et sont toujours considérés par la plupart des tests unitaires.
danidacar

2
danip: définitions claires sont importantes, et je n'insister sur eux. Mais il est également important de réaliser que ces définitions sont une cible. Ils valent la peine d’être visés, mais vous n’avez pas toujours besoin d’un point de mire. Les tests unitaires auront souvent des dépendances sur des bibliothèques de niveau inférieur.
Jeffrey Faust

2
De plus, il est important de comprendre le concept de test de façade, qui ne vérifie pas la manière dont les composants s'emboîtent (comme les tests d'intégration), mais teste un seul composant de manière isolée. (c'est-à-dire un graphe d'objets)
Ricardo Rodrigues

1
il ne s'agit pas seulement d'être plus rapide, mais aussi d'être extrêmement fastidieux ou ingérable, imaginez que, si vous ne le faites pas, votre arbre d'exécution moqueur pourrait connaître une croissance exponentielle. Imaginez que vous simuliez 10 dépendances dans votre unité si vous poussiez plus loin, disons que l'une des 10 dépendances est utilisée à de nombreux endroits et qu'elle possède 20 de ses propres dépendances. Vous devez donc vous moquer de 20 autres dépendances, potentiellement dupliquées à de nombreux endroits. à la dernière api, vous devez vous moquer - par exemple, une base de données, pour ne pas avoir à la réinitialiser et c'est plus rapide comme vous l'avez dit
FantomX1

39

Tester avec toutes les dépendances en place est toujours important, mais c'est davantage dans le domaine des tests d'intégration, comme l'a dit Jeffrey Faust.

L'un des aspects les plus importants des tests unitaires consiste à rendre vos tests fiables. Si vous ne croyez pas qu'un test réussi signifie vraiment que les choses vont bien et qu'un test qui échoue signifie vraiment un problème dans le code de production, vos tests ne sont pas aussi utiles qu'ils peuvent l'être.

Afin de rendre vos tests dignes de confiance, vous devez faire plusieurs choses, mais je vais me concentrer sur une seule pour cette réponse. Vous devez vous assurer qu’ils sont faciles à exécuter, afin que tous les développeurs puissent les exécuter facilement avant d’archiver le code. «Facile à exécuter» signifie que vos tests s’exécutent rapidement et qu’aucune configuration ni installation exhaustive n’est nécessaire pour les faire fonctionner. Idéalement, tout le monde devrait pouvoir consulter la dernière version du code, lancer les tests immédiatement et les voir réussir.

Résumer les dépendances sur d’autres éléments (système de fichiers, base de données, services Web, etc.) vous permet d’éviter la configuration et vous rend moins vulnérable, ainsi que d’autres développeurs, à des situations dans lesquelles vous êtes tenté de dire: «Les tests ont échoué car je ne le fais pas. pas le partage réseau mis en place. Oh bien. Je les exécuterai plus tard. "

Si vous souhaitez tester ce que vous faites avec certaines données, vos tests unitaires pour ce code de logique métier ne doivent pas se soucier de la manière dont vous obtenez ces données. Pouvoir tester la logique de base de votre application sans dépendre d'éléments tels que des bases de données est génial. Si vous ne le faites pas, vous manquez.

PS Je devrais ajouter qu'il est tout à fait possible de trop d'ingénieurs au nom de la testabilité. Tester votre application aide à atténuer cela. Quoi qu'il en soit, une mauvaise mise en œuvre d'une approche ne la rend pas moins valide. Tout peut être mal utilisé et exagéré si on ne continue pas à demander "pourquoi est-ce que je fais ça?" en développant.


En ce qui concerne les dépendances internes, les choses deviennent un peu boueuses. J'aime penser que je veux protéger autant que possible ma classe des changements pour de mauvaises raisons. Si j'ai une configuration comme celle-ci,

public class MyClass 
{
    private SomeClass someClass;
    public MyClass()
    {
        someClass = new SomeClass();
    }

    // use someClass in some way
}

Je ne me soucie généralement pas comment SomeClassest créé. Je veux juste l'utiliser. Si SomeClass change et nécessite maintenant des paramètres pour le constructeur ... ce n'est pas mon problème. Je ne devrais pas avoir à changer MyClass pour s'adapter à cela.

Maintenant, cela ne concerne que la partie conception. En ce qui concerne les tests unitaires, je souhaite également me protéger des autres classes. Si je teste MyClass, j'aime bien savoir qu'il n'y a pas de dépendances externes, que SomeClass n'a pas, à un moment donné, introduit une connexion à une base de données ou un autre lien externe.

Mais un problème encore plus important est que je sais aussi que certaines des résultats de mes méthodes reposent sur les résultats de certaines méthodes de SomeClass. Sans me moquer / bousculer SomeClass, je n’aurais peut-être aucun moyen de modifier cet apport en fonction de la demande. Si j'ai de la chance, je pourrai peut-être composer mon environnement dans le test de manière à déclencher la bonne réponse de SomeClass, mais cette méthode introduit une complexité dans mes tests et les rend fragiles.

Réécrire MyClass pour accepter une instance de SomeClass dans le constructeur me permet de créer une fausse instance de SomeClass qui renvoie la valeur que je veux (via un framework moqueur ou avec une maquette manuelle). Je n'ai généralement pas à introduire une interface dans ce cas. Le faire ou non est à bien des égards un choix personnel qui peut être dicté par la langue de votre choix (par exemple, les interfaces sont plus susceptibles en C #, mais vous n’auriez certainement pas besoin de Ruby).


+1 Excellente réponse, car les dépendances externes sont un cas auquel je ne pensais pas lorsque j'ai écrit la question initiale. J'ai clarifié ma question en réponse. Voir la dernière modification.
dsimcha

@dsimcha J'ai développé ma réponse pour aller plus en détail à ce sujet. J'espère que ça aide.
Adam Lear

Adam, pouvez-vous suggérer un langage dans lequel "Si SomeClass change et nécessite maintenant des paramètres pour le constructeur ... je ne devrais pas avoir à changer MyClass" est vrai? Désolé, cela m’a sauté aux yeux comme une exigence inhabituelle. Je peux voir que je n'ai pas à changer les tests unitaires pour MyClass, mais pas à changer MyClass ... wow.

1
@moz Ce que je voulais dire, c'est que si la manière dont SomeClass est créée, MyClass n'a pas à changer si SomeClass y est injecté au lieu d'être créé en interne. Dans des circonstances normales, MyClass ne devrait pas avoir à se soucier des détails de configuration de SomeClass. Si l'interface de SomeClass change, alors oui ... MyClass devra toujours être modifié si l'une des méthodes qu'elle utilise est affectée.
Adam Lear

1
Si vous vous moquez de SomeClass lors du test de MyClass, comment allez-vous détecter si MyClass utilise SomeClass de manière incorrecte ou repose sur une anomalie non testée de SomeClass susceptible de changer?
nom le

22

Outre le problème de test unitaire et d'intégration, prenez en compte les éléments suivants.

Le widget de classe a des dépendances sur les classes Thingamajig et WhatsIt.

Un test unitaire pour Widget échoue.

Dans quelle classe se situe le problème?

Si vous avez répondu "lancer le débogueur" ou "lire le code jusqu'à ce que je le trouve", vous comprenez l'importance de tester uniquement l'unité, pas les dépendances.


3
@ Brookok: Que diriez-vous de voir quels sont les résultats pour toutes les dépendances de Widget? S'ils réussissent tous, c'est un problème avec Widget jusqu'à preuve du contraire.
Dsimcha

4
@dsimcha, mais vous ajoutez maintenant de la complexité dans le jeu pour vérifier les étapes intermédiaires. Pourquoi ne pas simplifier et simplement faire d’abord des tests unitaires simples? Ensuite, faites votre test d'intégration.
asoundmove

1
@dsimcha, cela semble raisonnable jusqu'à ce que vous obteniez un graphe de dépendance non trivial. Supposons que vous ayez un objet complexe de 3 couches ou plus avec des dépendances, il se transforme en une recherche O (N ^ 2) du problème, plutôt qu'en O (1)
Brook

1
En outre, mon interprétation du PRS est qu'une classe devrait avoir une responsabilité unique au niveau du domaine conceptuel / du problème. Peu importe si s'acquitter de cette responsabilité l'oblige à faire beaucoup de choses différentes lorsqu'on le considère à un niveau d'abstraction inférieur. Si pris à l'extrême, le SRP serait contraire à OO parce que la plupart des classes contiennent des données et effectuent des opérations dessus (deux choses vues à un niveau suffisamment bas).
Dsimcha

4
@ Brookok: Plus de types n'augmente pas la complexité en soi. Davantage de types sont acceptables si ces types ont un sens conceptuel dans le domaine du problème et rendent le code plus facile à comprendre, pas plus difficile à comprendre. Le problème est de découpler artificiellement les éléments qui sont couplés conceptuellement au niveau du domaine du problème (c'est-à-dire que vous n'avez qu'une implémentation, vous n'en aurez probablement jamais plus d'une, etc.) et que le découplage ne correspond pas vraiment aux concepts du domaine du problème. Dans ces cas, il est ridicule, bureaucratique et prolixe de créer de graves abstractions autour de cette implémentation.
Dsimcha

14

Imaginez que la programmation ressemble à la cuisine. Ensuite, le test unitaire équivaut à s'assurer que vos ingrédients sont frais, savoureux, etc. Alors que le test d'intégration, c'est comme s'assurer que votre repas est délicieux.

En fin de compte, vous assurer que votre repas est délicieux (ou que votre système fonctionne) est la chose la plus importante, le but ultime. Mais si vos ingrédients, je veux dire, les unités, fonctionnent, vous aurez un moyen moins coûteux de le savoir.

En effet, si vous pouvez garantir le fonctionnement de vos unités / méthodes, vous aurez plus de chances de disposer d’un système opérationnel. J'insiste sur "plus probable", par opposition à "certain". Vous avez toujours besoin de vos tests d'intégration, tout comme vous avez encore besoin de quelqu'un pour goûter le repas que vous avez cuisiné et vous dire que le produit final est bon. Vous aurez plus de facilité à y arriver avec des ingrédients frais, c'est tout.


7
Et lorsque vos tests d'intégration échouent, le test unitaire peut cibler le problème.
Tim Williscroft

9
Pour étirer une analogie: lorsque vous trouvez que votre repas a un goût déplorable, il faut peut-être un peu d'enquête pour déterminer que c'est parce que votre pain est moisi. Le résultat, cependant, est que vous ajoutez un test unitaire pour vérifier la présence de pain moisi avant la cuisson, afin que ce problème particulier ne se reproduise plus. Ensuite, si vous avez un autre échec de repas plus tard, vous avez éliminé le pain moisi comme cause.
Kyralessa

Aimez cette analogie!
thehowler

6

Tester les unités en les isolant complètement permettra de tester TOUTES les variations possibles des données et des situations auxquelles cette unité peut être soumise. Comme il est isolé du reste, vous pouvez ignorer les variations n’ayant pas d’effet direct sur l’unité à tester. cela diminuera considérablement la complexité des tests.

Tester avec différents niveaux de dépendances intégrés vous permettra de tester des scénarios spécifiques qui n’auraient peut-être pas été testés lors des tests unitaires.

Les deux sont importants. En faisant simplement des tests unitaires, vous obtiendrez invariablement des erreurs subtiles plus complexes lors de l’intégration de composants. Effectuer des tests d’intégration signifie que vous testez un système sans avoir la certitude que les différentes parties de la machine n’ont pas été testées. Il est presque impossible de penser que vous pouvez obtenir un meilleur test complet en effectuant un test d'intégration, car plus vous ajoutez de composants, plus le nombre de combinaisons d'entrées augmente TRES rapidement (pensez factoriel) et il devient très rapidement impossible de créer un test avec une couverture suffisante.

En bref, les trois niveaux de "tests unitaires" que j'utilise généralement dans presque tous mes projets:

  • Tests unitaires, isolés avec des dépendances simulées ou tronquées pour tester l'enfer d'un seul composant. Idéalement, on tenterait une couverture complète.
  • Tests d'intégration pour tester les erreurs les plus subtiles. Vérifications ponctuelles minutieusement conçues montrant les cas limites et les conditions types. Une couverture complète est souvent impossible, les efforts étant axés sur ce qui ferait échouer le système.
  • Tests de spécification pour injecter diverses données réelles dans le système, les instrumenter (si possible) et observer le résultat pour s'assurer qu'il est conforme à la spécification et aux règles de gestion.

L'injection de dépendance est un moyen très efficace d'y parvenir car elle permet d'isoler très facilement des composants pour les tests unitaires sans ajouter de complexité au système. Votre scénario commercial ne justifie peut-être pas l’utilisation du mécanisme d’injection, mais votre scénario de test le fera presque. Cela me suffit pour le rendre indispensable. Vous pouvez également les utiliser pour tester indépendamment les différents niveaux d'abstraction via des tests d'intégration partiels.


4

Quel est le côté opposé à cela? Est-il vraiment important qu'un test unitaire ne teste pas également ses dépendances? Si oui, pourquoi?

Unité. Signifie singulier.

Tester 2 choses signifie que vous avez les deux choses et toutes les dépendances fonctionnelles.

Si vous ajoutez une 3ème chose, vous augmentez les dépendances fonctionnelles dans les tests au-delà de manière linéaire. Les interconnexions entre les choses se développent plus rapidement que le nombre de choses.

Il y a n (n-1) / 2 dépendances potentielles parmi n éléments testés.

C'est la grande raison.

La simplicité a de la valeur.


2

Rappelez-vous comment vous avez appris à faire la récursion? Mon prof a dit "Supposons que vous avez une méthode qui fait x" (par exemple, résout le fibbonacci pour tout x). "Pour résoudre pour x il faut appeler cette méthode pour x-1 et x-2". Dans le même ordre d'idées, le remplacement des dépendances vous permet de prétendre qu'elles existent et de vérifier que l'unité actuelle fait ce qu'elle devrait faire. L’hypothèse est bien sûr que vous testez les dépendances de manière aussi rigoureuse.

Ceci est essentiellement le SRP au travail. Se concentrer sur une seule responsabilité, même pour vos tests, supprime la quantité de jonglage mental que vous devez faire.


2

(Ceci est une réponse mineure. Merci @ Timwilliscroft pour l'indice.)

Les défauts sont plus faciles à localiser si:

  • L'unité sous test et chacune de ses dépendances sont testées indépendamment.
  • Chaque test échoue si et seulement s'il y a une erreur dans le code couvert par le test.

Cela fonctionne bien sur le papier. Cependant, comme illustré dans la description de l'OP (les dépendances sont boguées), si les dépendances ne sont pas testées, il serait difficile de localiser l'emplacement du défaut.


Pourquoi les dépendances ne seraient-elles pas testées? Ils sont probablement de niveau inférieur et plus faciles à tester en unité. De plus, avec les tests unitaires couvrant un code spécifique, il est plus facile de localiser l'emplacement du défaut.
David Thornley

2

Beaucoup de bonnes réponses. J'aimerais aussi ajouter quelques autres points:

Le test unitaire vous permet également de tester votre code lorsque vos dépendances n'existent pas . Par exemple, vous ou votre équipe n'avez pas encore écrit les autres couches ou attendez peut-être une interface fournie par une autre société.

Les tests unitaires signifient également qu'il n'est pas nécessaire de disposer d'un environnement complet sur votre machine de développement (base de données, serveur Web, etc.). Je fortement recommander que tous les développeurs n'ont un tel environnement, coupé mais vers le bas , il est, pour les bugs repro etc. Cependant, si pour une raison quelconque , il est impossible d'imiter l'environnement de production , puis les tests unitaires au moins yu donne un certain niveau de confiance dans votre code avant de passer à un système de test plus grand.


0

À propos de la conception: je pense que même les petits projets ont intérêt à rendre le code testable. Vous n’avez pas nécessairement besoin d’introduire quelque chose comme Guice (une classe d’usine simple le fera souvent), mais en séparant le processus de construction de la logique de programmation,

  • documenter clairement les dépendances de chaque classe via son interface (très utile pour les nouveaux venus dans l'équipe)
  • les classes deviennent beaucoup plus claires et faciles à maintenir (une fois que vous avez placé la création d'un graphe d'objet laid dans une classe séparée)
  • couplage lâche (rendant les changements beaucoup plus faciles)

2
Méthode: Oui, mais pour cela, vous devez ajouter du code et des classes supplémentaires. Le code doit être aussi concis que possible tout en restant lisible. À mon humble avis, l’ajout d’usines et tout le reste est une ingénierie excessive, à moins que vous ne puissiez prouver que vous avez besoin ou qu’il est très probable que vous ayez besoin de la flexibilité qu’il offre.
dsimcha

0

Euh ... il y a de bons points sur les tests unitaires et d'intégration écrits dans ces réponses ici!

Les vues pratiques et liées aux coûts me manquent . Cela dit, je vois clairement l'intérêt de tests unitaires très isolés / atomiques (peut-être très indépendants les uns des autres et avec la possibilité de les exécuter en parallèle et sans aucune dépendance, comme les bases de données, les systèmes de fichiers, etc.) et les tests d'intégration (de niveau supérieur), mais ... c'est aussi une question de coûts (temps, argent, ...) et de risques .

Donc, il y a d'autres facteurs, qui sont beaucoup plus importants (comme "quoi tester" ) avant de penser à "comment tester" d' après mon expérience ...

Mon client paie-t-il (implicitement) la quantité supplémentaire d'écriture et de maintenance des tests? Une approche davantage axée sur les tests (écrire des tests avant d’écrire le code) est-elle vraiment rentable dans mon environnement (analyse risque / coût des défaillances du code, personnes, spécifications de conception, environnement de test configuré)? Le code est toujours bogué, mais pourrait-il être plus rentable de déplacer les tests vers l'utilisation en production (dans le pire des cas!)?

Cela dépend aussi beaucoup de la qualité de votre code (normes), des frameworks, de l'EDI, des principes de conception, etc. que vous et votre équipe suivez et de leur expérience. Un code bien écrit, facile à comprendre, suffisamment documenté (idéalement auto-documenté), modulaire, ... introduit probablement moins de bogues que l'inverse. Ainsi, le «besoin» réel, la pression ou les coûts / risques généraux de maintenance / correction de bugs pour des tests approfondis peuvent ne pas être élevés.

Passons à l'extrême lorsqu'un collègue de mon équipe a suggéré, nous devons essayer de tester un peu notre code de couche modèle Java EE pur avec une couverture souhaitée de 100% pour toutes les classes de la base de données. Ou le responsable qui souhaite que les tests d'intégration soient couverts à 100% par tous les cas d'utilisation réels et les flux de travail Web ui, car nous ne voulons pas risquer de faire échouer un cas d'utilisation. Mais nous avons un budget serré d'environ 1 million d'euros, un plan assez serré pour tout coder. Un environnement client, où les bugs potentiels des applications ne constitueraient pas un très grand danger pour les humains ou les entreprises. Notre application sera testée en interne avec des tests unitaires importants, des tests d'intégration, des tests clients clés avec des plans de test conçus, une phase de test, etc. Nous ne développons pas d'application pour une usine nucléaire ou la production pharmaceutique!

J'essaie moi-même d'écrire test si possible et de développer tout en testant mon code. Mais je le fais souvent à partir d'une approche descendante (tests d'intégration) et j'essaie de trouver le bon point, celui de créer la "couche d'application" pour les tests importants (souvent dans la couche modèle). (car il y a souvent beaucoup de "couches")

De plus, le code de test unitaire et d'intégration n'a pas un impact négatif sur le temps, l'argent, la maintenance, le codage, etc. Il est cool et devrait être appliqué, mais avec précaution et en tenant compte des implications lorsque vous démarrez ou après 5 ans d'un grand nombre de tests développés. code.

Donc, je dirais que cela dépend vraiment beaucoup de la confiance et de l’évaluation coûts / risques / avantages ... comme dans la vraie vie où vous ne pouvez pas et ne voulez pas courir avec beaucoup ou à 100% de mécanismes de sécurité.

L'enfant peut et doit monter quelque part et peut tomber et se blesser. La voiture peut cesser de fonctionner parce que j'ai fait le mauvais carburant (entrée non valide :)). Le pain grillé peut être brûlé si le bouton de l'heure cesse de fonctionner après 3 ans. Mais je n'ai pas, jamais, envie de conduire sur l'autoroute et de tenir mon volant détaché entre mes mains :)

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.