Подтвердить что ты не робот

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

Вариант 1: установка языка по умолчанию

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

Задайте опцию из продукта → Схема → Управление схемами или ⌘⇧,. Затем выберите вкладку "Параметры" и установите язык.

Xcode - Установить язык приложений по умолчанию

Преимущества: простое одноразовое изменение.

Против. Невозможно использовать локализованные скриншоты с snapshot (инструмент, который запускает ваше приложение через UI Testing и создает скриншоты в App Store на этом пути).

Вариант 2: используйте -accessibilityIdentifier для локализованных строк

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

Затем вы можете продолжить установку accessibilityLabel и accessibilityValue, как обычно, с локализованными версиями.

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

Минусы. Возможно, потребуется больше работы, чтобы изменить каждую метку, которая вам нужна "нелокализованная" для тестирования.

Ответ 3

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

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

В целевом меню "Тестирование пользовательских интерфейсов" → "Сформировать фазы" → "Ресурсы копирования пакетов", добавьте необходимые файлы локализации (например, 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

Ответ 4

Простейшим и надежным способом для меня до сих пор является ссылка на элементы с 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()

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

Ответ 5

Ответ Volodymyr мне очень помог, но он может потерпеть неудачу, если имя папки пакета локализации отличается от набора deviceLanguage, установленного в Snapshot. Этот снипп отлично работает для меня в Swift 3.0 и с языками, такими как итальянский (где текущая локаль "это", но язык устройства "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
}

Ответ 6

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

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

Ответ 7

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

- (void)setUp
{
    [super setUp];

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

Ответ 8

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

Если вы используете базовую локализацию, то ваши Localizable.strings могут быть не локализованы на вашем базовом языке. В этом нет необходимости, поскольку в этом случае будет использоваться базовый язык. Если это так, функция SeanRs 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 "?"
}

Ответ 9

Для функции моментального снимка 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 ""
}

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

Ответ 10

Решение 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;
}