Abstraction des dépendances héritées et natives de l’environnement Apple
Imaginez que vous ayez une ancienne partie de votre application iOS qui nécessite de nouvelles fonctionnalités et des adaptations à certains flux à des fins réglementaires.
De plus, vous disposez d’un système d’intégration continue qui bloque les demandes d’extraction qui ne contiennent pas suffisamment de tests, de sorte que chaque fonction et classe doit être testée unitairement de manière efficace et significative.
Le problème est que le projet contient beaucoup de code hérité et que les classes reposent sur des classes natives qui n’ont pas de protocoles ou d’inversion de dépendance, ce qui les rend impossibles à tester de la manière souhaitée.
Par exemple, considérons une classe qui utilise le NotificationCenter
diffuser des messages aux observateurs. Pour avoir un test significatif et vérifier toutes les sorties possibles, vous devez affirmer que si une classe est un observateur pour cet événement, l’événement doit y être géré.
Cependant, étant donné qu’Apple n’a pas conçu de protocole pour la NotificationCenter
il y a un couplage à cette classe et aucun moyen d’injecter un mock ou un spy qui reçoit le message de la classe testing :
Outre la classe de test, pour fonctionner, nous devons garantir la NotificationCenter
fonctionne comme prévu, et nous n’avons aucun moyen d’affirmer si l’événement a été correctement publié en raison de l’encapsulation du NotificationCenter
.
Pour affirmer notre méthode à l’intérieur Class
envoyé l’événement de notification correctement, nous avions besoin d’un protocole pour simuler le NotificationCenter
et vérifier les sorties à partir de là. Mais attendez, Apple ne nous a pas fourni de protocole, alors est-ce impossible ? Pas du tout, nous pouvons toujours le faire de notre côté !
Nous désirons donc une maquette pour notre NotificationCenter
droite? Eh bien, tout ce dont nous avons besoin est de connaître les méthodes de base de notre classe de dépendances et de créer un protocole pour chacune, puis nous pourrons faire en sorte que notre classe de dépendances signe ce protocole et se moque de chaque sortie à tester.
Penser à NotificationCenter
il y a deux méthodes qui sont importantes pour nous : addObserver
et post
enregistrant respectivement un objet observateur et envoyant une notification. :
Maintenant, nous avons créé un protocole pour chacune de nos méthodes pour les tester individuellement.
Nous expliquerons plus tard pourquoi nous créons un protocole par méthode, pour l’instant, rendons nos dépendances existantes conformes à celles-ci :
Voyez que nous n’avons même pas implémenté les méthodes puisqu’elles existent déjà à l’intérieur de la classe. Notez que ce que nous avons fait est d’établir un contrat pour le code qui est déjà concret.
Maintenant que nous avons un contrat pour le NotificationCenter
vérifions d’abord ce dont notre classe de test a besoin et utilisons le bon protocole.
Vérifiez que, comme nous n’appelons pas le addObserver
méthode de ce côté, nous n’avons pas besoin de déclarer notre dépendance avec cette exigence.
Par défaut, nous injectons déjà le NotificationCenter.default
exemple afin de ne rien casser ailleurs.
Créons maintenant une maquette (ou un espion si vous préférez) pour recevoir les sorties de la classe de test, puis comparons dans le XCTest
classer.
Maintenant, si nous testons MyClass
nous pouvons vérifier quelles sont les sorties reçues dans le NotificationCenter
car nous nous appuyons maintenant sur une simulation.
Super, testons notre classe :
Nous pouvons maintenant comparer les name
variable facultative à l’intérieur de la simulation avec un résultat attendu ou vérifiez si ce n’est pas le cas nil
.
Si notre classe testée s’appelait addObserver
méthode à un endroit, nous aurions besoin de notre dépendance pour se conformer à NotificationCenterAddingObserver
protocole deux afin d’avoir également ses sorties. Supposons MyClass
s’appuie également sur la addObserver
et vous voulez tester s’il a été enregistré :
Maintenant, nous déclarons notre dépendance en tant que conformité aux deux protocoles en même temps et nous devrions mettre à jour chaque endroit qui fait référence au type de notificationCenter
.
Si nous avons besoin d’un simulacre pour cela, il doit maintenant se conformer aux deux protocoles et contenir toutes les valeurs possibles :
Dans cet article, j’ai présenté une méthode pour extraire les dépendances héritées et natives de l’environnement Apple. J’ai utilisé l’exemple d’un centre de notification et répertorié chacune des méthodes dont ma classe testée a besoin à partir de la dépendance.
J’ai ensuite établi un contrat, ou protocole, pour me permettre de déboguer les sorties de ma classe. Si votre objectif est d’écrire des tests unitaires de haute qualité, la clé est d’améliorer le principe de responsabilité unique et l’inversion des dépendances, testant ainsi uniquement ce qui appartient à la portée isolée de ma classe. J’espère que vous êtes maintenant capable de créer du code testable et que vous avez apprécié la lecture de cet article.