Objet fictif - Mock object

Dans la programmation orientée objet , les objets fictifs sont des objets simulés qui imitent le comportement d'objets réels de manière contrôlée, le plus souvent dans le cadre d'une initiative de test logiciel . Un programmeur crée généralement un objet fictif pour tester le comportement d'un autre objet, de la même manière qu'un concepteur automobile utilise un mannequin de crash test pour simuler le comportement dynamique d'un humain lors d'impacts de véhicules. La technique est également applicable en programmation générique .

Motivation

Dans un test unitaire , les objets fictifs peuvent simuler le comportement d'objets réels complexes et sont donc utiles lorsqu'un objet réel est peu pratique ou impossible à intégrer dans un test unitaire. Si un objet présente l'une des caractéristiques suivantes, il peut être utile d'utiliser un objet fictif à sa place :

  • l'objet fournit des résultats non déterministes (par exemple l'heure courante ou la température courante) ;
  • il a des états difficiles à créer ou à reproduire (par exemple une erreur de réseau) ;
  • il est lent (par exemple une base de données complète , qu'il faudrait initialiser avant le test) ;
  • il n'existe pas encore ou peut changer de comportement ;
  • il devrait inclure des informations et des méthodes exclusivement à des fins de test (et non pour sa tâche réelle).

Par exemple, un programme de réveil qui fait sonner une cloche à une certaine heure peut obtenir l'heure actuelle d'un service de temps. Pour tester cela, le test doit attendre l'heure de l'alarme pour savoir s'il a sonné correctement. Si un service d'heure fictive est utilisé à la place du service en temps réel, il peut être programmé pour fournir l'heure de sonnerie (ou toute autre heure) indépendamment de l'heure réelle, afin que le programme de réveil puisse être testé de manière isolée.

Détails techniques

Les objets fictifs ont la même interface que les objets réels qu'ils imitent, permettant à un objet client de ne pas savoir s'il utilise un objet réel ou un objet fictif. De nombreux frameworks d'objets fictifs disponibles permettent au programmeur de spécifier quelles méthodes et dans quel ordre seront invoquées sur un objet fictif et quels paramètres leur seront transmis, ainsi que quelles valeurs seront renvoyées. Ainsi, le comportement d'un objet complexe tel qu'une prise réseau peut être imité par un objet fictif, permettant au programmeur de découvrir si l'objet testé répond de manière appropriée à la grande variété d'états dans lesquels ces objets fictifs peuvent se trouver.

Moquettes, contrefaçons et talons

La classification entre les simulations, les contrefaçons et les talons est très incohérente dans la littérature. Cependant, il est constant dans la littérature qu'ils représentent tous un objet de production dans un environnement de test en exposant la même interface.

Lequel parmi mock , fake ou stub est le plus simple est incohérent, mais le plus simple renvoie toujours des réponses pré-arrangées (comme dans une méthode stub ). De l'autre côté du spectre, l'objet le plus complexe simulera entièrement un objet de production avec une logique complète, des exceptions, etc. Que l'un des trios fictifs, faux ou stub corresponde ou non à une telle définition est, encore une fois, incohérent à travers le Littérature.

Par exemple, une implémentation de méthode fictive, fausse ou stub entre les deux extrémités du spectre de complexité peut contenir des assertions pour examiner le contexte de chaque appel. Par exemple, un objet fictif peut affirmer l'ordre dans lequel ses méthodes sont appelées ou affirmer la cohérence des données entre les appels de méthode.

Dans le livre The Art of Unit Testing, les simulations sont décrites comme un faux objet qui aide à décider si un test a échoué ou réussi en vérifiant si une interaction avec un objet s'est produite. Tout le reste est défini comme un talon. Dans ce livre, les contrefaçons sont tout ce qui n'est pas réel, qui, en fonction de leur utilisation, peut être soit des talons, soit des moqueries .

Définir les attentes

Prenons un exemple où un sous-système d'autorisation a été simulé. L'objet fictif implémente une isUserAllowed(task : Task) : booleanméthode pour correspondre à celle de la classe d'autorisation réelle. De nombreux avantages découlent s'il expose également une isAllowed : booleanpropriété, qui n'est pas présente dans la classe réelle. Cela permet au code de test de définir facilement l'attente qu'un utilisateur recevra ou non une autorisation lors du prochain appel et donc de tester facilement le comportement du reste du système dans les deux cas.

De même, les paramètres fictifs uniquement pourraient garantir que les appels ultérieurs au sous-système le feront lever une exception , se bloquer sans répondre, ou retourner, nulletc. Ainsi, il est possible de développer et de tester les comportements des clients pour des conditions d'erreur réalistes dans le back-end sous-systèmes ainsi que pour leurs réponses attendues. Sans un système de simulation aussi simple et flexible, tester chacune de ces situations peut être trop laborieux pour qu'elles soient dûment prises en compte.

Écriture de chaînes de journal

La save(person : Person)méthode d'un objet de base de données fictive peut ne pas contenir beaucoup (le cas échéant) de code d'implémentation. Il peut vérifier l'existence et peut-être la validité de l'objet Person transmis pour la sauvegarde (voir la discussion fake vs mock ci-dessus), mais au-delà de cela, il pourrait n'y avoir aucune autre implémentation.

C'est une opportunité manquée. La méthode fictive pourrait ajouter une entrée à une chaîne de journal publique. L'entrée n'a pas besoin d'être plus que "Personne enregistrée", ou elle peut inclure certains détails de l'instance d'objet de personne, comme un nom ou un ID. Si le code de test vérifie également le contenu final de la chaîne de journal après diverses séries d'opérations impliquant la base de données fictive, il est alors possible de vérifier que dans chaque cas exactement le nombre attendu de sauvegardes de la base de données a été effectué. Cela peut trouver des bogues qui sapent les performances autrement invisibles, par exemple, lorsqu'un développeur, inquiet de perdre des données, a codé des appels répétés save()là où un seul aurait suffi.

Utilisation dans le développement piloté par les tests

Les programmeurs travaillant avec la méthode de développement piloté par les tests (TDD) utilisent des objets fictifs lors de l'écriture de logiciels. Les objets fictifs répondent aux exigences d' interface et remplacent les objets réels plus complexes ; ainsi, ils permettent aux programmeurs d'écrire et de tester des fonctionnalités dans un domaine sans appeler des classes sous-jacentes ou collaboratrices complexes . L'utilisation d'objets fictifs permet aux développeurs de concentrer leurs tests sur le comportement du système testé sans se soucier de ses dépendances. Par exemple, tester un algorithme complexe basé sur plusieurs objets étant dans des états particuliers peut être clairement exprimé en utilisant des objets fictifs à la place d'objets réels.

Outre les problèmes de complexité et les avantages tirés de cette séparation des préoccupations , il existe des problèmes pratiques de vitesse impliqués. Développer un logiciel réaliste à l'aide de TDD peut facilement impliquer plusieurs centaines de tests unitaires. Si beaucoup d'entre eux induisent une communication avec des bases de données, des services Web et d'autres systèmes hors processus ou en réseau , alors la suite de tests unitaires deviendra rapidement trop lente pour être exécutée régulièrement. Cela conduit à son tour à de mauvaises habitudes et à une réticence du développeur à maintenir les principes de base du TDD.

Lorsque des objets fictifs sont remplacés par des objets réels, la fonctionnalité de bout en bout devra être testée davantage. Il s'agira de tests d'intégration plutôt que de tests unitaires.

Limites

L'utilisation d'objets fictifs peut coupler étroitement les tests unitaires à l'implémentation du code qui est testé. Par exemple, de nombreux frameworks d'objets fictifs permettent au développeur de vérifier l'ordre et le nombre de fois que les méthodes d'objets fictifs ont été invoquées par l'objet réel testé ; une refactorisation ultérieure du code en cours de test pourrait donc provoquer l'échec du test même si toutes les méthodes d'objet simulé obéissent toujours au contrat de l'implémentation précédente. Cela montre que les tests unitaires doivent tester le comportement externe d'une méthode plutôt que son implémentation interne. L'utilisation excessive d'objets fictifs dans le cadre d'une suite de tests unitaires peut entraîner une augmentation considérable de la maintenance à effectuer sur les tests eux-mêmes pendant l'évolution du système au fur et à mesure de la refactorisation. La maintenance incorrecte de tels tests au cours de l'évolution pourrait permettre de rater des bogues qui seraient autrement détectés par des tests unitaires utilisant des instances de classes réelles. À l'inverse, se moquer simplement d'une méthode peut nécessiter beaucoup moins de configuration que la configuration d'une classe réelle entière et donc réduire les besoins de maintenance.

Les objets simulés doivent modéliser avec précision le comportement de l'objet dont ils se moquent, ce qui peut être difficile à réaliser si l'objet moqué provient d'un autre développeur ou projet ou s'il n'a même pas encore été écrit. Si le comportement n'est pas modélisé correctement, les tests unitaires peuvent enregistrer une réussite même si un échec se produirait au moment de l'exécution dans les mêmes conditions que le test unitaire exerce, rendant ainsi le test unitaire inexact.

Voir également

Les références

Liens externes