Xcode 7 UITests with localized UI

后端 未结 11 909
既然无缘
既然无缘 2020-12-07 17:38

In my App I\'m using NSLocalizedString to localize my app. Now I want to switch to UITests and habe Testcode like this:

[tabBarsQue         


        
相关标签:
11条回答
  • 2020-12-07 17:38

    The simplest and reliable way for me so far is to reference elements with elementBoundByIndex() Like this:

        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()
    

    You can guess/experiment with this values and find elements you need.

    0 讨论(0)
  • 2020-12-07 17:38

    For fastlane's snapshot feature, the SnapshotHelper.swift launches app with these arguments. So by interpreting these values, this solution is deterministic, and I was able to produce correct snapshots for multiple languages:

    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 ""
    }
    

    Hope this helps

    0 讨论(0)
  • 2020-12-07 17:48

    Objective-C Solution : inspired by @Volodymyr Prysiazhniuk solution

    - (NSString*)getLocalizedStringForKey :(NSString*)stringKey forUITestClass : (id) uiTestClass{
        if (!stringKey || !uiTestClass){
            return nil;
        }
        NSString *bundlePath = [[NSBundle bundleForClass: uiTestClass]bundlePath];
        NSBundle* bundle = [NSBundle bundleWithPath:bundlePath];
        NSString* localizedString = NSLocalizedStringWithDefaultValue(stringKey, nil, bundle, nil, nil);
        return localizedString;
    }
    
    0 讨论(0)
  • 2020-12-07 17:50

    YOU CAN RE-USE YOUR PROJECT LOCALIZATION BUNDLES!

    When you test message boxes behaviour you need to know exactly what message box just appeared. You need to copy your localization from another scheme during build phase.

    In your UI Tests target -> Build Phases -> Copy Bundle Resources, add the localization files needed (e.g. Localizable.strings).

    Add a function similar to the following:

    func localizedString(key:String) -> String {
    /*1*/ let localizationBundle = NSBundle(path: NSBundle(forClass: /*2 UITestsClass*/.self).pathForResource(deviceLanguage, ofType: "lproj")!) 
    /*3*/ let result = NSLocalizedString(key, bundle:localizationBundle!, comment: "") // 
        return result
    }
    
    /*1 Gets correct bundle for the localization file, see here: http://stackoverflow.com/questions/33086266/cant-get-access-to-string-localizations-in-ui-test-xcode-7 */
    /*2 Replace this with a class from your UI Tests 
    /*3 Gets the localized string from the bundle */
    

    Then in your code you can use app.buttons[localizedString("localized.string.key")]

    Full article is here: https://github.com/fastlane-old/snapshot/issues/321#issuecomment-159660882

    0 讨论(0)
  • 2020-12-07 17:51

    I wanted to actually test the content of UI features and not just their existence, so setting a default language or using the accessibility identifiers wouldn't suit.

    This builds on Volodymyr's and matsoftware's answers. However their answers rely on deviceLanguage which needs to be explicitly set in SnapshotHelper. This solution dynamically gets the actual supported language the device is using.

    1. Add the Localizable.strings files to your UITest target.
    2. Add the following code to your UITest target:

      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. Access the method by localizedString(key)

    For those languages with a script code, the localeCode will be langCode-scriptCode (for example, zh-Hans). Otherwise the localeCode will be langCode-regionCode (for example, pt-BR). The testBundle first tries to resolve the lproj by localeCode, then falls back to just langCode.

    If it still can't get the bundle, it returns "?" for the string, so it will fail any UI tests that look for specific strings.

    0 讨论(0)
  • 2020-12-07 17:51

    The answer of SeanR is great (+1), but there is a minor improvement:

    If you use base localization, then your Localizable.strings might not be localized in your base language. This is not necessary because the base language would be used in this case. If so, SeanR’s function localizedString would return „?“.

    The extended version below checks additionally for the base language, and returns the localized string in the base language:

    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 讨论(0)
提交回复
热议问题