Dans son discours sur TDD, où tout a mal tourné , Ian Cooper pousse l'intention originale de Kent Beck derrière les tests unitaires en TDD (pour tester les comportements, pas les méthodes de classes en particulier) et plaide pour éviter de coupler les tests à la mise en œuvre.
Dans le cas d'un comportement comme save X to some data source
dans un système avec un ensemble typique de services et de référentiels, comment pouvons-nous tester à l'unité la sauvegarde de certaines données au niveau du service, via le référentiel, sans coupler le test aux détails d'implémentation (comme appeler une méthode spécifique )? Est-ce que le fait d'éviter ce type de couplage ne vaut pas vraiment la peine / le mal d'une certaine façon?
unit-testing
tdd
coupling
Andy Hunt
la source
la source
Réponses:
Votre exemple spécifique est un cas que vous devez généralement tester en vérifiant si une certaine méthode a été appelée, car cela
saving X to data source
signifie communiquer avec une dépendance externe , donc le comportement que vous devez tester est que la communication se produit comme prévu .Cependant, ce n'est pas une mauvaise chose. Les interfaces limites entre votre application et ses dépendances externes ne sont pas des détails d'implémentation , en fait, elles sont définies dans l'architecture de votre système; ce qui signifie qu'une telle frontière n'est pas susceptible de changer (ou si c'est le cas, ce serait le type de changement le moins fréquent). Ainsi, coupler vos tests à une
repository
interface ne devrait pas vous poser trop de problèmes (si c'est le cas, pensez si l'interface ne dérobe pas des responsabilités à l'application).Maintenant, considérez uniquement les règles métier d'une application, découplées de l'interface utilisateur, des bases de données et d'autres services externes. C'est là que vous devez être libre de modifier la structure et le comportement du code. C'est là que le couplage des tests et des détails d'implémentation vous obligera à modifier plus de code de test que le code de production, même lorsqu'il n'y a aucun changement dans le comportement global de l'application. C'est là que les tests
State
au lieu deInteraction
nous aider à aller plus vite.PS: Je n'ai pas l'intention de dire si le test par État ou Interactions est le seul vrai moyen de TDD - je pense qu'il s'agit d'utiliser le bon outil pour le bon travail.
la source
Mon interprétation de cet exposé est:
Ce n'est pas indiqué dans l'exposé, mais je pense que le contexte supposé des conseils est quelque chose comme:
Ainsi, tester un composant est la plus grande étendue possible dans laquelle quelque chose peut encore être raisonnablement appelé test unitaire. C'est assez différent de la façon dont certaines personnes, en particulier les universitaires, utilisent le terme. Cela ne ressemble en rien aux exemples du didacticiel type d'outil de test unitaire. Il correspond cependant à son origine dans les tests matériels; les cartes et modules sont testés à l'unité, pas les fils et les vis. Ou du moins vous ne construisez pas un faux Boeing pour tester une vis ...
Extrapoler à partir de cela, et jeter certaines de mes propres pensées,
Si vous le faites correctement et proprement, vous avez à peine besoin d'un outil moqueur; il n'est utilisé que quelques fois par système.
Une base de données est généralement un collaborateur, elle est donc falsifiée plutôt que raillée. Ce serait pénible à mettre en œuvre à la main; heureusement, de telles choses existent déjà .
Le modèle de test de base consiste à effectuer une séquence d'opérations (par exemple, enregistrer et recharger un document); confirmez que cela fonctionne. C'est la même chose que pour tout autre scénario de test; aucun changement d'implémentation (fonctionnel) n'est susceptible d'entraîner l'échec d'un tel test.
L'exception est où les enregistrements de la base de données sont écrits mais jamais lus par le système testé; par exemple, les journaux d'audit ou similaires. Ce sont des sorties et doivent donc être moquées. Le modèle de test consiste à effectuer une séquence d'opérations; confirmer que l'interface d'audit a été appelée avec les méthodes et les arguments spécifiés.
Notez que même ici, à condition que vous utilisiez un outil de simulation de type sécurisé comme mockito , renommer une méthode d'interface ne peut pas entraîner un échec de test. Si vous utilisez un IDE avec les tests chargés, il sera refactorisé avec la méthode renommer. Si vous ne le faites pas, le test ne sera pas compilé.
la source
Ma suggestion est d'utiliser une approche de test basée sur l'état:
DONNÉ Nous avons la DB de test dans un état connu
QUAND le service est appelé avec des arguments X
ALORS Affirmez que la base de données est passée de son état d'origine à l'état attendu en appelant des méthodes de référentiel en lecture seule et en vérifiant leurs valeurs renvoyées
De cette façon, vous ne comptez sur aucun algorithme interne du service et êtes libre de refactoriser son implémentation sans avoir à modifier les tests.
Le seul couplage ici est à l'appel de méthode de service et aux appels de référentiel nécessaires pour lire les données de la base de données, ce qui est bien.
la source