Xcode 7 essais avec L'interface utilisateur localisée

Dans mon Application j'utilise NSLocalizedString localiser mon application. Maintenant je veux passer à UITests et habe Testcode comme ceci:

[tabBarsQuery.buttons["particiants"] tap];

cela fonctionne pour l'anglais mais ne fonctionne pas pour les autres langues.

[tabBarsQuery.buttons[NSLocalizedString("PARTICIPANTS",comment:nil)] tap];

échoue-probablement parce que localisable.strings est dans un autre paquet. Comment tester une application localisée?

25
demandé sur svmrajesh 2015-11-09 15:40:07

9 réponses

je voulais tester le contenu des fonctionnalités de L'interface utilisateur et pas seulement leur existence, donc définir un langage par défaut ou utiliser les identificateurs d'accessibilité ne conviendrait pas.

cela s'appuie sur Volodymyr 's et matsoftware 's answers. Cependant, leurs réponses s'appuient sur deviceLanguage qui doit être explicitement défini dans SnapshotHelper . Cette solution obtient de façon dynamique le langage pris en charge que le périphérique utilise.

  1. ajoutez les fichiers Localizable.strings à votre cible UITest.
  2. ajouter le code suivant à votre cible UITest:

    var currentLanguage: (langCode: String, localeCode: String)? {
        let currentLocale = Locale(identifier: Locale.preferredLanguages.first!)
        guard let langCode = currentLocale.languageCode else {
            return nil
        }
        var localeCode = langCode
        if let scriptCode = currentLocale.scriptCode {
            localeCode = "\(langCode)-\(scriptCode)"
        } else if let regionCode = currentLocale.regionCode {
            localeCode = "\(langCode)-\(regionCode)"
        }
        return (langCode, localeCode)
    }
    
    func localizedString(_ key: String) -> String {
        let testBundle = Bundle(for: /* a class in your test bundle */.self)
        if let currentLanguage = currentLanguage,
            let testBundlePath = testBundle.path(forResource: currentLanguage.localeCode, ofType: "lproj") ?? testBundle.path(forResource: currentLanguage.langCode, ofType: "lproj"),
            let localizedBundle = Bundle(path: testBundlePath)
        {
            return NSLocalizedString(key, bundle: localizedBundle, comment: "")
        }
        return "?"
    }
    
  3. accéder à la méthode par localizedString(key)

pour les langues avec un code de script, le localeCode sera langCode-scriptCode (par exemple, zh-Hans ). Autrement le localeCode sera langCode-regionCode (par exemple, pt-BR ). Le testBundle essaie d'abord de résoudre le lproj par localeCode , puis retombe juste langCode .

S'il ne peut toujours pas obtenir le paquet, il retourne "?"pour la chaîne, donc il échouera tous les tests D'interface utilisateur qui cherchent des chaînes spécifiques.

6
répondu SeanR 2017-05-23 12:02:51

Option 1: Définir une langue par défaut

créer un nouveau schéma pour le test D'interface utilisateur et définir le langage d'Application par défaut. Cela va verrouiller l'application dans un fichier localisé afin que vous puissiez écrire tous vos tests pour cette langue.

définir L'option du produit - > schéma - > Gérer les schémas ou ⌘⇧, . Ensuite, sélectionnez l'onglet Options et choisir la langue.

Xcode - Set the Default Application Language

Pros : Simple, un changement de temps.

Cons : ne peut pas être utilisé pour créer des captures d'écran localisées avec snapshot (un outil qui exécute votre application via un test D'interface utilisateur et génère des captures D'écran de boutique D'applications le long du chemin).

Option 2: Utiliser -accessibilityIdentifier pour les chaînes localisées

au lieu d'accéder aux articles via leur affichage texte ou valeur, utilisez accessibilityIdentifier . Ceci est lu par le cadre de test de L'UI mais jamais montré ou lu aux utilisateurs (même avec l'accessibilité activée). Dans L'ancienne UIAutomation Docs Apple mentionne l'utilisation de cette fonctionnalité pour les développeurs, ce qui se présente comme un bon cas d'utilisation.

vous pouvez alors continuer à définir accessibilityLabel et accessibilityValue comme normal, avec les versions localisées.

Pros : peut être utilisé pour des solutions plus génériques, comme prendre des captures d'écran automatisées.

Cons : peut nécessiter plus de travail changer chaque étiquette dont vous avez besoin "non localisé" pour les tests.

22
répondu Joe Masilotti 2015-11-09 17:44:27

VOUS POUVEZ RÉUTILISER VOS FAISCEAUX DE LOCALISATION DE PROJET!

lorsque vous testez le comportement des boîtes à messages, vous devez savoir exactement quelle boîte à messages vient d'apparaître. Vous devez copier votre localisation à partir d'un autre schéma pendant la phase de construction.

dans vos tests D'interface utilisateur target - > Build Phases - > Copy Bundle Resources, add the localization files neededed (e.g. localisable.chaîne.)

Ajouter une fonction similaire à la

    func localizedString (key: String) - > String {
/*1*/ laissez localizationBundle = NSBundle(chemin d'accès: NSBundle(forClass: /*2 UITestsClass*/.auto.)pathForResource (deviceLanguage, ofType: "lproj")!) 
/*3*/ laissez résultat = NSLocalizedString(clé, bundle:localizationBundle!, commentaire: "") // 
    résultat de retour
}

/*1 obtient le paquet correct pour le fichier de localisation, voir ici: /q/peut-t-get-access-to-string-localisations-en-ui-test-xcode-7-23891/"localisée.chaîne.la clé")]  

article complet est ici: https://github.com/fastlane-old/snapshot/issues/321#issuecomment-159660882

10
répondu Volodymyr Prysiazhniuk 2016-06-30 22:08:28

la manière la plus simple et fiable pour moi jusqu'à présent est de faire référence à des éléments avec elementBoundByIndex() Comme ceci:

    let app = XCUIApplication()
    let tabBar = app.tabBars
    tabBar.buttons.elementBoundByIndex(2).tap()
    app.navigationBars.buttons.elementBoundByIndex(0).tap()
    app.tables.cells.elementBoundByIndex(2).tap()
    app.tables.elementBoundByIndex(1).cells.elementBoundByIndex(0).tap()

vous pouvez deviner/expérimenter avec ces valeurs et trouver les éléments dont vous avez besoin.

5
répondu Vladimir Shutyuk 2015-11-18 18:17:34

la réponse de Volodymyr m'a beaucoup aidé, mais elle peut échouer si le nom du dossier du paquet de localisation diffère du devicelanguage défini dans Snapshot. Cet extrait fonctionne très bien pour moi dans Swift 3.0 et avec des langues comme l'italien (où le local actuel est "it" mais le langage de périphérique est "it-IT").

    func localizedString(key:String) -> String {
      let languageBundlePath = Bundle(for: PlinthUITests.self).path(forResource: deviceLanguage, ofType: "lproj") ?? Bundle(for: PlinthUITests.self).path(forResource: NSLocale.current.languageCode!, ofType: "lproj")
      let localizationBundle = Bundle(path: languageBundlePath!)
      let result = NSLocalizedString(key, bundle:localizationBundle!, comment: "")
    return result
}
2
répondu matsoftware 2016-10-29 18:09:05

si vous faites cela dans le but de lancer Snapshot (plutôt que L'UI test), alors je trouve la solution la plus simple est de tricher et d'utiliser HSTestingBackchannel 151930920"

c'est un outil que j'ai écrit qui vous permet d'envoyer des notifications de la classe UITesting à l'application. Ensuite, vous écrivez du code dans l'application qui répond directement aux notifications.

0
répondu Confused Vorlon 2016-05-05 18:04:35

en plus de la réponse de Joe, vous pouvez aussi forcer le langage pour les tests D'UI directement dans le code de test sans éditer un schéma comme celui-ci:

- (void)setUp
{
    [super setUp];

    self.continueAfterFailure = NO;
    XCUIApplication *app = [[XCUIApplication alloc] init];
    app.launchArguments = @[@"-AppleLanguages", @"(en)", @"-AppleLocale", @"en_EN"];
    [app launch];
}
0
répondu Leszek Szary 2017-06-26 14:39:45

la réponse de SeanR est grande (+1), mais il y a une amélioration mineure:

si vous utilisez la localisation de base, alors votre Localizable.strings pourrait ne pas être localisé dans votre langue de base. Cela n'est pas nécessaire car la langue de base serait utilisée dans ce cas. Si c'est le cas, la fonction de SeanR localizedString renvoie „?“ .

la version étendue ci-dessous vérifie en plus pour la langue de base, et renvoie la chaîne localisée dans la base langue:

func localizedString(_ key: String) -> String {
    let testBundle = Bundle(for: ShopEasyUITests.self)
    guard let currentLanguage = currentLanguage else { return "?" }
    if let testBundlePath = testBundle.path(forResource: currentLanguage.localeCode, ofType: "lproj"),
        let localizedBundle = Bundle(path: testBundlePath) {
        return NSLocalizedString(key, bundle: localizedBundle, comment: "")
    }
    if let testBundlePath = testBundle.path(forResource: currentLanguage.langCode, ofType: "lproj"),
        let localizedBundle = Bundle(path: testBundlePath) {
        return NSLocalizedString(key, bundle: localizedBundle, comment: "")
    }
    if let testBundlePath = testBundle.path(forResource: "Base", ofType: "lproj"),
        let localizedBundle = Bundle(path: testBundlePath) {
        return NSLocalizedString(key, bundle: localizedBundle, comment: "")
    }
    return "?"
}
0
répondu Reinhard Männer 2018-01-31 21:31:56

pour la fonction snapshot de fastlane, le SnapshotHelper.swift lance l'application avec ces arguments. Ainsi, en interprétant ces valeurs, Cette solution est déterministe, et j'ai pu produire des instantanés corrects pour plusieurs langues:

func getLocale(str: String) -> String {
    let start = str.index(str.startIndex, offsetBy: 1)
    let end = str.index(start, offsetBy: 2)
    let range = start..<end

    var locale = str.substring(with: range)
    if locale == "en" {
        return "Base"
    }
    return locale
}

func localizedString(_ key: String) -> String {
    print("app.launchArguments \(app.launchArguments)")
    guard let localeArgIdx = app.launchArguments.index(of: "-AppleLocale") else {
        return ""
    }
    if localeArgIdx >= app.launchArguments.count {
        return ""
    }
    let str = app.launchArguments[localeArgIdx + 1]
    let locale = getLocale(str: str)
    let testBundle = Bundle(for: Snapshot.self)
    if let testBundlePath = testBundle.path(forResource: locale, ofType: "lproj") ?? testBundle.path(forResource: locale, ofType: "lproj"),
        let localizedBundle = Bundle(path: testBundlePath)
    {
        return NSLocalizedString(key, bundle: localizedBundle, comment: "")
    }
    return ""
}

Espérons que cette aide

0
répondu tsuz 2018-04-16 12:32:58