Xcode 7 UITests с локализованным пользовательским интерфейсом
В моем приложении я использую NSLocalizedString
для локализации моего приложения. Теперь я хочу переключиться на UITests
и тест-код habe следующим образом:
[tabBarsQuery.buttons["particiants"] tap];
Это работает на английском языке, но не работает на других языках.
[tabBarsQuery.buttons[NSLocalizedString("PARTICIPANTS",comment:nil)] tap];
Сбой - возможно, потому что Localizable.strings - в другом комплекте. Как проверить локализованное приложение?
Ответы
Ответ 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;
}