it-swarm.com.ru

Xcode 7 UITests с локализованным пользовательским интерфейсом

В моем приложении я использую NSLocalizedString для локализации своего приложения. Теперь я хочу переключиться на UITests и иметь тестовый код следующим образом:

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

Это работает для английского языка, но не для других языков.

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

Сбой - вероятно, потому что Localizable.strings находится в другом комплекте. Как я могу проверить локализованное приложение?

25
netshark1000

Я хотел на самом деле протестировать содержимое функций пользовательского интерфейса, а не только их существование, поэтому установка языка по умолчанию или использование идентификаторов доступности не подойдет.

Это основано на ответах Volodymyr и matsoftware . Однако их ответы основаны на deviceLanguage, который необходимо явно указать в SnapshotHelper. Это решение динамически получает фактический поддерживаемый язык, который использует устройство.

  1. Добавьте файлы Localizable.strings к вашей цели UITest.
  2. Добавьте следующий код к вашей цели 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. Доступ к методу осуществляется с помощью localizedString(key)

Для языков с кодом скрипта localeCode будет langCode-scriptCode (например, zh-Hans). В противном случае localeCode будет langCode-regionCode (например, pt-BR). testBundle сначала пытается разрешить lproj с помощью localeCode, затем возвращается к langCode.

Если он все еще не может получить пакет, он возвращает "?" для строки, поэтому он не пройдет все тесты пользовательского интерфейса, которые ищут конкретные строки.

8
SeanR

Вариант 1: Legen Sie eine Standardsprache fest

Erstellen Sie ein neues Схема для проведения тестов и мероприятий, посвященных стандартному фестивалю. Dadurch wird die App in einer lokalisierten Datei gespeichert, sodass Sie alle Ihre Tests for the diese Sprache schreiben können.

Stellen Sie die Вариант unter Produkt -> Схема -> Схемы вербальные или надписей⌘⇧,, Wählen Sie dann die Registerkarte Опционально и в полном объеме.

Xcode - Set the Default Application Language

Vorteile : Einfache, einmalige Änderung.

Nachteile : Kann nicht zum Erstellen lokalisierter Снимки экрана mit Schnappschuss (einem Tool, mit dem Ihre App über UI-тесты ausgeführt und der App Store generiert wird) Скриншоты для веб-сайтов).

Вариант 2: Verwenden Sie -accessibilityIdentifier für lokalisierte Zeichenfolgen

Verwenden Sie accessibilityIdentifier, anstatt über den angezeigten Текст сообщения Wert auf Elemente zuzugreifen. Dies wird vom UI Testing-Framework gelesen, aber niemals Benutzern angezeigt oder vorgelesen (auch bei aktivierter Eingabehilfe). В полном смысле слова Apple, компания Apple умирает, так как она была очень важна.

Anschließend können Sie accessibilityLabel und accessibilityValue wie gewohnt mit den lokalisierten Versionen festlegen.

Vorteile : Kann für allgemeinere Lösungen verwendet werden, z. B. für automatisierte Скриншоты.

Nachteile : Könnte mehr Arbeit erfordern, wenn Sie jedes Etikett ändern, das Sie zum Testen "nicht lokalisiert" benötigen.

24
Joe Masilotti

ВЫ МОЖЕТЕ ИСПОЛЬЗОВАТЬ БАНКИ ЛОКАЛИЗАЦИИ ПРОЕКТА!

При тестировании поведения окон сообщений вам необходимо точно знать, какое окно сообщений только что появилось. Вам нужно скопировать вашу локализацию из другой схемы на этапе сборки.

В вашей цели UI Tests -> Build Phases -> Copy Bundle Resources добавьте необходимые файлы локализации (например, Localizable.strings).

Добавьте функцию, аналогичную следующей:

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 */

Тогда в вашем коде вы можете использовать app.buttons [localizedString ("localized.string.key")]

Полная статья здесь: https://github.com/fastlane-old/snapshot/issues/321#issuecomment-159660882

11
Volodymyr Prysiazhniuk

На данный момент самый простой и надежный способ для меня - ссылаться на элементы с помощью elementBoundByIndex () Вот так: 

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

Вы можете угадать/поэкспериментировать с этими значениями и найти нужные вам элементы.

6
Vladimir Shutyuk

Мне очень помог ответ Владимира, но он может потерпеть неудачу, если имя папки комплекта локализации отличается от имени устройства, установленного в Снимке. Этот фрагмент прекрасно работает для меня в Swift 3.0 и с такими языками, как итальянский (где текущая локаль - «это», но язык устройства - «это-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
matsoftware

Если вы делаете это с целью запуска Snapshot (а не реального тестирования пользовательского интерфейса), то я считаю, что самое простое решение - обмануть и использовать HSTestingBackchannel

Это инструмент, который я написал, который позволяет отправлять уведомления из класса UITesting в приложение. Затем вы пишете код в приложении, которое отвечает непосредственно на уведомления.

0
Confused Vorlon

Ответ SeanR отличный (+1), но есть небольшое улучшение: 

Если вы используете базовую локализацию, то ваш Localizable.strings может быть не локализован на вашем базовом языке. В этом нет необходимости, поскольку в этом случае будет использоваться базовый язык. Если это так, функция SeanR localizedString вернет „?“

Расширенная версия ниже дополнительно проверяет базовый язык и возвращает локализованную строку на базовом языке: 

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
Reinhard Männer

Решение Objective-C: Вдохновлено решением @Volodymyr Prysiazhniuk

- (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
Skander Fathallah

В дополнение к ответу Джо вы также можете принудительно использовать язык для тестов пользовательского интерфейса непосредственно в тестовом коде, не редактируя такую ​​схему:

- (void)setUp
{
    [super setUp];

    self.continueAfterFailure = NO;
    XCUIApplication *app = [[XCUIApplication alloc] init];
    app.launchArguments = @[@"-AppleLanguages", @"(en)", @"-AppleLocale", @"en_EN"];
    [app launch];
}
0
Leszek Szary

Для функции моментального снимка fastlane SnapshotHelper.Swift запускает приложение с этими аргументами. Таким образом, интерпретируя эти значения, это решение является детерминированным, и я смог создать правильные снимки для нескольких языков:

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

Надеюсь это поможет

0
tsuz