【问题标题】:UI Test (XCTest) with localized app.staticTexts带有本地化 app.staticTexts 的 UI 测试 (XCTest)
【发布时间】:2016-02-18 14:15:12
【问题描述】:

我正在为我的应用编写 UI 测试,它的内容使用 UIWebView。当 webview 中的按钮被按下时,它会编写如下测试用例:

func testExample() {
    let app = XCUIApplication()
    app.staticTexts["Log In"].tap()
}

这里的问题是,如果应用程序在不同的本地化中(例如“de-DE”),那么带有文本“登录”的按钮不存在。取而代之的是它的“Anmelden”。 我尝试了以下方法,但它们都不起作用:

Localizable.strings 版本:

func testExample() {
    let name = NSLocalizedString("Log In", comment: "")

    let app = XCUIApplication()
    app.staticTexts[name].tap()
}

public class Titles {
    var homeScreenLogIn = "Log In"
}

public class TitlesDE: Titles {

    override init() {
        super.init()

        homeScreenLogIn = "Anmelden"
    }

}

...

func testExample() {
    titles = TitlesDE()

    let name = titles.homeScreenLogIn

    let app = XCUIApplication()
    app.staticTexts[name].tap()
}

UI 测试失败 - 找到多个匹配项:

问题还在于我似乎无法弄清楚如何将数据转储到调试/测试输出,因为print() 似乎不起作用。

【问题讨论】:

    标签: ios swift xctest xcode-ui-testing


    【解决方案1】:

    您应该使用所有UIKit 对象都具有的accessibilityIdentifier 属性,因为此字符串未本地化,它仅用于 UI 自动化。

    也就是说,当您使用UIWebView 的内容时,似乎没有一个简单的解决方案。

    【讨论】:

      【解决方案2】:

      我终于在测试函数中用这段代码实现了:

      let locale = Locale.current.identifier
          print(locale)
      
          var tap1 = "STRING OF UI TEST 1 in ENGLISH"
          var tap2 = "STRING OF UI TEST 2 in ENGLISH"
      
          if (locale == "fr_US" || locale == "fr" || locale == "fr_FR" || locale == "fr_CA") {
              tap1 = "STRING OF UI TEST 1 in FRENCH"
              tap2 = "STRING OF UI TEST 2 in FRENCH"
          }
          else if (locale == "es_US" || locale == "es_ES" || locale == "es") {
              tap1 = "STRING OF UI TEST 1 in SPANISH"
              tap2 = "STRING OF UI TEST 2 in SPANISH"
          }
      

      只需在生成 UITest 的代码中,更改我们刚刚创建的变量(tap1 和 tap2)的“字符串”。

      就我而言:

      let textView = scrollViewsQuery.otherElements.containing(.staticText, identifier:tap1).children(matching: .textView).element
      

      我使用的是 SWIFT 4。

      希望对你有帮助:)

      【讨论】:

      • 总体思路很扎实,但实现很糟糕。首先,locale.hasPrefix("es") 对你有很大帮助。
      • 您好 Sulthan,感谢您的反馈。您所说的问题在于它不适用于像 es-MX 这样的语言,因为它在“es”中,但是本地化发生了变化。除此之外,这只需要自动制作屏幕截图,所以它不是代码本身。做得这么好并不重要,但如果有另一种方式会更好:)
      【解决方案3】:

      这是我的解决方案,我将字符串对象扩展为包含 .localized()...您可能可以忽略(或更改)所有 bundleName 逻辑,因为它特定于我的项目,其中 shell 脚本移动/重命名本地化以非常具体的方式捆绑,但基本上,您需要从设备的当前设置语言中获取捆绑的目录名称。另请注意,要完成这项工作,我必须在“复制捆绑资源”下的 UI 测试目标中添加应用程序的 Localizable.strings 和 Localizable.stringsdict

      用例: app.buttons["点击我!".localized()].tap() app.staticTexts["App内显示的本地化字符串值"].tap()

      请记住,您调用 .localized() 的字符串必须在应用程序的 Localizable 包中定义,如果不是,则它不是在设备语言更改时会翻译的字符串。

      扩展字符串{

      func localized() -> String {
      
          // all this shit with bundleName is here only because we have a shell script that
          // renames/moves all of our localization folders
          var bundleName:String = deviceLanguage.stringByReplacingOccurrencesOfString("_", withString: "-")
          let secondaryBundleNameParts = deviceLanguage.componentsSeparatedByString("-")
          let secondaryBundleNamePartOne = secondaryBundleNameParts[0]
          let secondaryBundleNamePartTwo = secondaryBundleNameParts.count > 1 ? secondaryBundleNameParts[1] : ""
          let thirdBundleName = "\(secondaryBundleNamePartOne)-\(secondaryBundleNamePartTwo.uppercaseString)"
          if secondaryBundleNamePartOne == "es" {
              bundleName = secondaryBundleNamePartTwo == "ES" ? "es" : thirdBundleName
          }
          else if secondaryBundleNamePartOne == "pt" {
              bundleName = secondaryBundleNamePartTwo == "BR" ? "pt" : thirdBundleName
          }
          else if secondaryBundleNamePartOne == "zh" {
              bundleName = secondaryBundleNamePartTwo == "CN" ? "zh-Hans" : "zh-Hant"
          }
          else {
              bundleName = secondaryBundleNamePartOne
          }
          var bundlePath:String? = NSBundle(forClass: FGUITestCase.self).pathForResource(bundleName, ofType: "lproj")
          if bundlePath == nil {
              bundlePath = NSBundle(forClass: FGUITestCase.self).pathForResource(deviceLanguage, ofType: "lproj")
              if bundlePath == nil {
                  bundlePath = NSBundle(forClass: FGUITestCase.self).pathForResource(bundleName, ofType:nil)
                  if bundlePath == nil {
                      bundlePath = NSBundle(forClass: FGUITestCase.self).pathForResource(deviceLanguage, ofType:nil)
                      if bundlePath == nil {
                          for var i=0; i<100; i++ {
                              NSLog("OMG, WTF, Localization Bundle Not Found!: \(bundleName) || \(deviceLanguage)")
                              print("OMG, WTF, Localization Bundle Not Found!: \(bundleName) || \(deviceLanguage)")
                          }
                      }
                  }
              }
          }
      
          let bundle = NSBundle(path:bundlePath!)
          return NSLocalizedString(self, bundle:bundle!, comment: "")
      }
      
      subscript (i: Int) -> Character {
          return self[self.startIndex.advancedBy(i)]
      }
      
      subscript (i: Int) -> String {
          return String(self[i] as Character)
      }
      
      subscript (r: Range<Int>) -> String {
          let start = startIndex.advancedBy(r.startIndex)
          let end = start.advancedBy(r.endIndex - r.startIndex)
          return self[Range(start: start, end: end)]
      }
      

      }

      【讨论】:

        【解决方案4】:

        我能够处理这个问题的方法是创建一个Localization 类,它包含不同 UI 元素的翻译字典,可以使用 String 键提取,该键对于 Web 视图元素是唯一的你的应用程序。

        要使用的本地化是使用全局变量localization 设置的,它可以在测试期间的任何时候设置,例如在setUp()期间。

        使用枚举表示本地化。

        注意:这是一个相当粗略的实现,因为字典上的验证方式并不多 - 创建一个本地化集类并使用 [String: LocalizedIdentifierSet] 字典将是一个更可靠的解决方案.

        var localization = .Germany
        
        class Localization {
        
            /// Dictionary containing localizations for each available localisation
            private static var localizations: [String: [Localization: String]] = [
                "signOutLink": [
                    .UnitedKingdom: "SIGN OUT",
                    .UnitedStates: "SIGN OUT",
                    .France: "DÉCONNEXION",
                    .Germany: "ABMELDEN",
                    .Italy: "ESCI",
                    .Spain: "SALIR",
                    .Australia: "SIGN OUT",
                    .Russia: "ВЫЙТИ"
                ],
                "appSettingsLink": [
                    .UnitedKingdom: "App Settings",
                    .UnitedStates: "App Settings",
                    .France: "Réglages",
                    .Germany: "App-Einstellungen",
                    .Italy: "Impostazioni dell'App",
                    .Spain: "Ajustes de la App",
                    .Australia: "App Settings",
                    .Russia: "Ajustes de la App"
                ]
            ]
        
        
            /**
            Returns a String containing the localized identifier for the element with the given `identifier`, for the currently-selected `localization`.
        
            - Parameter identifier: String identifier for the element you want to retrieve the localized query string for.
        
            - Returns: String to be used for querying the view hierarchy to find the element with `identifier`.
            */
            class func getLocalizedQueryStringForElementWithIdentifier(identifier: String) -> String? {
                let localizationsForElement = localizations[identifier]
                let queryString = localizationsForElement?[localization]
                return queryString
            }
        }
        

        然后您将使用此类来检索您在查询中使用的标识符:

        let textIdentifier = Localization.getLocalizedQueryStringForElementWithIdentifier("signOutLink")
        let textElement = app.staticTexts[textIdentifier!]
        textElement.tap()
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-12-10
          • 2017-04-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-11-19
          • 1970-01-01
          相关资源
          最近更新 更多