Introduction
Les doublures de tests permettent d’isoler les classes à tester et de briser les intéraction entre elles. Les doublures de tests ne remplace pas JUnit et permet de tester les appels que la classe va faire aux autres classes.
Par exemple, imaginons que l’on a une classe Messagerie
qui peut lire
les messages d’un·e utilisateur·ice sur base de son identifiant et mot
de passe. Pour vérifier l’utliisateur·ice, la Messagerie va faire appel
à la classe Identification
. Cependant ici on ne veut tester uniquement
Messagerie mais pas la classe d’Identification.
Pour avoir plus d’explications vous pouvez aller voir cet article (le code en exemple n’y est pas forcément de la meilleure des qualités mais c’est pratique pour comprendre le principe des doublures).
Pourquoi utiliser une doublure ?
Il y a plusieurs raisons pour laquelle on voudrait mettre en place une doublure :
- La classe testée fait appel à un composant dificile ou couteux à
mettre en place (par exemple une base de donnée, c’est nottament le
cas pour notre classe
Identification
dans l’exemple) - Le test vise à vérifier le comportement d’une classe dans une situation exceptionnelle (imaginons, une déconnexion réseau, on ne vas pas réellement déconnecter la machine juste pour faire un test)
- La classe testée fait appel à un composant qui n’existe pas encore ou
qui n’est pas suffisament stable (cela permet par exemple de tester
notre classe
Messagerie
alors que la classeIdentification
dont elle dépends n’existe pas encore) - Le test fait appel à du code lent (par exemple, si notre Identification prends un certain temps, cela ralentirait grandement les tests pour rien)
- Le test fait appel à du code non déterministe (par exemple à l’heure ou à l’aléatoire. Par exemple si une classe ferait appel à une classe générant des nombres entre 1 et 6, nos tests serait faux 5 fois sur 6)
- Séparer le code de test du code de l’application (par exemple si on
crée une méthode
fakeGenerateNumber()
dans une classe aléatoire, cela complique les choses pour rien)
Types de doublures
Les stub objects
Un stub est simplement une classe écrite à la main spécialement pour le contexte du test. On sait quelle valeurs sont attendues d’avance et on les hardcode dans la classe.
Par exemple, dans le cas de l’idenficiation de et de la messagerie on
peut avoir une interface Identification
et créer une classe
IdentificationStub
implémentant cette interface de façon à hardcoder
les valeurs attendues pour le test (par exemple) :
Pour le test il suffit alors simplement d’injecter la classe
IdentificationStub
dans le constructeur de la classe Messagerie
.
Les fake objects
Un fake est une doublure écrite à la main qui implémente le comportement attendu mais de façon plus simple que la classe réelle. Contrairement au stub qui est écrit spécialement pour un test précis, le fake a vocation à être suffisament générique pour être utilisé dans plusieurs tests. Il est donc plus complexe que le stub mais plus réutilisable.
Pour reprendre l’exemple précédent on peut imaginer une classe
IdentificationFake
qui implémente Identification
mais qui a une
méthode supplémentaire addAccount(String username, String password)
permettant de personaliser le test.
Comme pour le stub on peut donc aller l’injecter dans le constructeur lors du test, à la différence qu’ici on peut l’utiliser pour plusieurs tests différents et le configurer différemment à chaque fois.
Les dummy objects
Les dummy sont le type de doublure le plus simple, ce sont simplement des classes implémentant l’interface attendue mais ne faisant absolument rien car ils ne sont jamais vraiment utilisés.
Par exemple si on teste un cas précis où l’identification n’est jamais
utilisée on peut créer une classe dummy implémentant Identification
et
qui renverrai toujours la même valeur (car quelque soit la valeur on
s’en fout puis ce qu’elle ne sera pas utilisée) :
Ici le code du test se fait exactement comme pour le stub
Les mock objects
Les mocks objects sont plus complexes mais plus flexibles que les autres et c’est ceux là que l’on va priviléger pour l’activité intégrative en utilisant la librarie Mockito.
Contrairement aux autres, les mocks sont générés par une librarie, on a donc pas besoin de créer la classe nous même, il suffit juste de dire dans notre test que l’on souhaite créer un Mock et quelle valeur on veut que certaines méthodes retournent.
Ainsi les Mocks ont la simplicité des Fake objects mais sans avoir à créer la moindre classe soi-même.
Pour l’exemple précédent à la place de créer tout une classe on a simplement à définir ceci dans le test :
Libraries
Il existe 2 librairies principales pour faire du mocking en Java, mais ici c’est Mockito qui a été privilégié.
Mockito
- facile d’utilisation
- configuration via annotation simple
- très grande communauté
- Choisi pour le cours
La documentation de Mockito est assez affreuse mais au moins elle est là, vous pouvez retrouver quelques liens intéressants sur leur site, ainsi que leur documentation officielle.
EasyMock
- Facile d’utilisation
- Configuration simple mais plus chiant que l’autre
- Moins utilisé que mockito
Spy objects
Les spy objects permettent de vérifier qu’une méthode à été
appellée, de savoir combien de fois et avec quels arguments. Pour
reprendre l’exemple précédent, si on imagine que la méthode
identify
est void, et ne retourne donc rien; on pourra tout de
même la tester en vérifiant qu’elle a bien été appellée avec les
bons arguments.
Cela peut être fait dans un Fake object mais est beaucoup plus compliqué à mettre en place, cela est en revanche trivial à faire avec Mockito :
De plus Mockito permet également d’espioner de vrais objets.