Partir sur des bases solides pour faire des tests unitaires est un sujet qui nécessite un peu de réflexion. En effet, le cycle de vie d’un test unitaire et comment XCTest le gère , peut augmenter le temps pris et la complexité de nos tests si on gère pas ce cycle correctement.

La classe AppDelegate constitue le point d’entrée de nos applications iOS et peut contenir éventuellement plusieurs traitements nécessaires au lancement de l’application (Appel API, lecture de données depuis la remote config, persistance de données….). Lorsqu’on on lance nos tests unitaires, XCTest fait appel à notre AppDelegate. Par défaut notre application constitue le host de nos tests unitaires.

Définir l’application comme host des tests unitaires
Lancement de la méthode DidFinishLaunchingWithOptions

Dans cet article, on essayera de présenter une solution connue pour utiliser un fake AppDelegate à la place de notre appdelegate par défaut pour exécuter nos tests unitaires indépendamment en respectant du principe d’isolation dans les tests.

  • Création d’un fake App Delegate:

Au niveau de notre target de tests, on peut ajouter un dossier helpers dans lequel on ajoutera une classe qu’on nommera : AppdelegateTesting qui constitue notre fake AppDelegate, elle sera identique à notre appDelegate mais elle contiendra seulement le strict minimum pour passer nos testes unitaires

import UIKit

@objc(AppdelegateTesting)
class AppdelegateTesting: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        return true
    }
}

Maintenant, la question qui se pose, comment je peux informer Xcode qu’il faut pointer sur cette classe dans les tests unitaires au lieu de l’Appdelegate original ?

La réponse est simple, il faut juste supprimer l’annotation ou l’attribut @main au niveau de notre appdelegate de base et redéfinir la classe main.swift!!!!

  • Quel est le rôle de @main?

Dans tous les programmes, vous avez toujours un point d’entrée, à partir duquel votre application doit commencer, et Swift ne fait pas l’exception. Si vous vous souvenez des très vieux jours avec Objective C, nous avions le fichier main.m ci dessous:

int main(int argc, char *argv[])
{
    @autoreleasepool {
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

Dans Swift, l’annotation @main permet d’indiquer au compilateur que cette classe/structure est le point d’entrée de l’application. D’ailleurs, il n’est pas nécessaire de définir un fichier main.swift pour ça. Du coup on peut avoir une structure de ce type:

@main 
class MyApp {
 static func main() {
     /// On va définir notre appDelegate ici
    }
}

Le compilateur vous indiquera qu’il faut définir une méthode static main() vu que vous avez ajouté l’annotation @main.

  • Basculer entre les deux classes en référence à l’AppDelegate

Au niveau de notre fonction statique main, on peut ajouter ce bout de code

@main
class MyApp {
    static func main() {
        let appDelegateClass: AnyClass = NSClassFromString("AppdelegateTesting") ?? AppDelegate.self
        UIApplicationMain(CommandLine.argc,
                          CommandLine.unsafeArgv,
                          nil,
                         NSStringFromClass(appDelegateClass)
        )
    }
}

Tout d’abord on va définir une variable appDelegateClass qui permet de déterminer quelle classes d’AppDelegate on va utiliser. Elle sera sera déterminer selon le contexte d’exécution ( qui sera déterminé au runtime à travers le bundle). Lorsqu’on on lance nos tests à travers CMD + U, le compilateur sait qu’il est dans un bundle de test (dans le target de tests), alors il va ainsi créer un objet de type AppdelegateTesting. En lançant pour un CMD + R, on sera dans le bundle de l’application et il va créer un objet AppDelegate

Pour lancer notre Appdelegate, on passe par la fonction UIApplicationMain(_:_:_:_:) qui a comme 4ème paramètre le nom de la classe AppDelegate définie au runtime.

Lorsque nous lançons maintenant nos tests, on pointera directement sur le fake appDelegate

Conclusion

Nous avons vu comment on peut gérer le cycle de vie de lancement d’une application iOS et comment on peut utiliser une Appdelegate séparée.

Cela vous permettra de préparer un environnement de tests isolé au maximum de l’application.

Références:

iOS Unit testing by Example – John Reid

https://developer.apple.com/documentation/uikit/1622933-uiapplicationmain

https://medium.com/@abedalkareemomreyh/what-is-main-in-swift-bc79fbee741c

Leave A Comment

Solve : *
27 ⁄ 3 =