具有本地化app.staticTexts的UI测试(XCTest)

时间:2016-02-18 14:15:12

标签: ios swift xctest xcode-ui-testing

我正在为我的应用编写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()似乎无法正常工作。

4 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:0)

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

用例: app.buttons [“Tap Me!”。localized()]。tap() app.staticTexts [“在App中显示的本地化字符串值”] .swap()

请记住,您正在调用.localized()的字符串必须在应用程序的Localizable包中定义,如果不是,那么它就不是在设备语言更改时将转换的字符串。

extension String {

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)]
}

}

答案 2 :(得分:0)

我能够解决这个问题的方法是创建一个Localization类,它包含不同UI元素的翻译字典,可以使用String键提取,该字符串是唯一的。您应用中的网络视图元素。

要使用的本地化是使用全局变量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()

答案 3 :(得分:0)

我终于在测试功能中通过以下代码实现了它:

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。

希望它会有所帮助:)