web-dev-qa-db-fra.com

Test de l'interface utilisateur iOS sur une vue isolée

J'essaie d'incorporer des tests d'interface utilisateur dans mon projet iOS, mais une chose qui continue de me retenir est le fait qu'il semble que tous les tests que vous écrivez doivent commencer depuis le début de l'application et se poursuivre. Par exemple, si je veux tester une vue qui se trouve derrière un écran de connexion, mes tests doivent d'abord s'exécuter sur l'écran de connexion, entrer un nom d'utilisateur/mot de passe, cliquer sur connexion, puis aller à la vue que je veux tester. Idéalement, les tests pour la vue de connexion et la suivante seraient complètement isolés. Existe-t-il un moyen de le faire, ou la philosophie derrière les tests d'interface utilisateur me manque-t-elle complètement?

31
mike

Absolument!

Vous avez besoin d'un environnement d'application propre dans lequel vous pouvez exécuter vos tests - une ardoise vierge.

Toutes les applications ont un délégué d'application qui définit l'état initial de l'application et fournit un contrôleur de vue racine au lancement. Pour les tests, vous ne voulez pas que cela se produise - vous devez pouvoir tester de manière isolée, sans que toutes ces choses se produisent. Idéalement, vous voulez pouvoir effectuer un test d'écran et uniquement cet écran chargé, et aucun autre changement d'état ne se produit.

Pour ce faire, vous pouvez créer un objet juste pour les tests qui implémente UIApplicationDelegate. Vous pouvez indiquer à l'application de s'exécuter en "mode de test" et d'utiliser le délégué d'application spécifique aux tests à l'aide d'un argument de lancement.

Objectif-C: main.m:

int main(int argc, char * argv[]) {
NSString * const kUITestingLaunchArgument   = @"org.quellish.UITestingEnabled";

    @autoreleasepool {
        if ([[NSUserDefaults standardUserDefaults] valueForKey:kUITestingLaunchArgument] != nil){
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([TestingApplicationDelegate class]));
        } else {
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([ProductionApplicationDelegate class]));
        }
    }
}

Swift: main.Swift:

let kUITestingLaunchArgument = "org.quellish.UITestingEnabled"

if (NSUserDefaults.standardUserDefaults().valueForKey(kUITestingLaunchArgument) != nil){
    UIApplicationMain(Process.argc, Process.unsafeArgv, NSStringFromClass(UIApplication), NSStringFromClass(TestingApplicationDelegate))

} else {
    UIApplicationMain(Process.argc, Process.unsafeArgv, NSStringFromClass(UIApplication), NSStringFromClass(AppDelegate))
}

Vous devrez supprimer tout @UIApplicationMain annotation de vos classes Swift.

Pour les "tests d'application", assurez-vous de définir l'action "Test" du schéma dans Xcode pour fournir l'argument de lancement:

Xcode Scheme editor

Pour les tests d'interface utilisateur, vous pouvez définir les arguments de lancement dans le cadre du test:

Objectif c:

XCUIApplication *app = [[XCUIApplication alloc] init];
[app setLaunchArguments:@[@"org.quellish.UITestingEnabled"] ];
[app launch];

Rapide:

let app = XCUIApplication()
app.launchArguments = [ "org.quellish.UITestingEnabled" ]
app.launch()

Cela permet aux tests d'utiliser un délégué d'application spécifiquement pour les tests. Cela vous donne beaucoup de contrôle - vous avez maintenant une ardoise vierge avec laquelle travailler pour les tests. Le délégué de l'application de test peut charger un storyboard spécifique ou mettre en place un UIViewController vide. Dans le cadre de vos tests d'interface utilisateur, vous pouvez instancier le contrôleur de vue sous test et le définir comme contrôleur de vue racine du keyWindow ou le présenter de manière modale. Une fois qu'il a été ajouté ou présenté, vos tests peuvent s'exécuter et, une fois terminé, supprimez-le ou supprimez-le.

25
quellish

Si cela ne vous dérange pas le chargement de l'interface utilisateur d'origine, passez simplement à l'interface utilisateur cible avec:

override func setUp() {
    super.setUp()
    continueAfterFailure = false
    XCUIApplication().launch()
    let storyboard = UIStoryboard(name: "MainStoryboard", bundle: NSBundle.mainBundle())
    let controller = storyboard.instantiateViewControllerWithIdentifier("LanguageSelectController")
    UIApplication.sharedApplication().keyWindow?.rootViewController = controller
}

Si vous ne voulez pas que l'interface utilisateur d'origine se charge, passez également ceci à partir de votre test:

app.launchArguments.append("skipEntryViewController")

puis dans didFinishLaunchingWithOptions, vous pouvez vérifier:

if NSProcessInfo.processInfo().arguments.contains("skipEntryViewController") {
    // then do NOT call makeKeyAndVisible
}
7
William Entriken