ios - unit - XCode 7 UITests con UI localizada
unit tests swift (9)
Opción 1: establecer un idioma predeterminado
Cree un nuevo esquema para las pruebas de UI y establezca el idioma de aplicación predeterminado. Esto bloqueará la aplicación en un archivo localizado para que pueda escribir todas sus pruebas para ese idioma.
Configure la opción en Producto -> Esquema -> Administrar esquemas o ⌘⇧,. Luego selecciona la pestaña Opciones y configura el idioma.
Pros : Simple, cambio de una sola vez.
Contras : no se puede utilizar para crear capturas de pantalla localizadas con snapshot (una herramienta que ejecuta su aplicación a través de UI Testing y genera capturas de pantalla de la App Store a lo largo del camino).
Opción 2: usar -accessibilityIdentifier
para cadenas localizadas
En lugar de acceder a los elementos a través de su texto o valor mostrado, use accessibilityIdentifier
. Esto lo lee el marco de UI Testing, pero nunca se muestra ni se lee a los usuarios (incluso con la accesibilidad activada). En los documentos antiguos de UIAutomation, Apple menciona el uso de esta para la funcionalidad de desarrollador, lo que parece un buen caso de uso.
Luego puede continuar configurando accessibilityLabel
y accessibilityValue
como normal, con las versiones localizadas.
Pros : Se puede utilizar para soluciones más genéricas, como tomar capturas de pantalla automatizadas.
Contras : Podría requerir más trabajo cambiando cada etiqueta que necesita "no localizada" para las pruebas.
En mi aplicación, estoy usando NSLocalizedString
para localizar mi aplicación. Ahora quiero cambiar a UITests
y habe Testcode así:
[tabBarsQuery.buttons["particiants"] tap];
Esto funciona para el inglés pero falla para otros idiomas.
[tabBarsQuery.buttons[NSLocalizedString("PARTICIPANTS",comment:nil)] tap];
Fallos, probablemente porque Localizable.strings está en otro paquete. ¿Cómo puedo probar una aplicación localizada?
Además de la respuesta de Joe, también puede forzar el idioma para las pruebas de UI directamente en el código de prueba sin editar un esquema como este:
- (void)setUp
{
[super setUp];
self.continueAfterFailure = NO;
XCUIApplication *app = [[XCUIApplication alloc] init];
app.launchArguments = @[@"-AppleLanguages", @"(en)", @"-AppleLocale", @"en_EN"];
[app launch];
}
La forma más sencilla y confiable para mí hasta ahora es hacer referencia a elementos con elementBoundByIndex (). Así:
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()
Puedes adivinar / experimentar con estos valores y encontrar los elementos que necesites.
La respuesta de SeanR es excelente (+1), pero hay una pequeña mejora:
Si usa la localización base, es posible que sus Localizable.strings
no estén localizadas en su idioma base. Esto no es necesario porque el lenguaje base se usaría en este caso. Si es así, la función localizedString
SeanR devolvería „?“
.
La versión extendida a continuación verifica adicionalmente el idioma base, y devuelve la cadena localizada en el idioma base:
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 "?"
}
La respuesta de Volodymyr me ayudó mucho, pero puede fallar si el nombre de la carpeta del paquete de localización difiere del dispositivo Idioma establecido en Instantánea. Este fragmento de código funciona bien para mí en Swift 3.0 y con idiomas como el italiano (donde la configuración regional actual es "it" pero el idioma del dispositivo es "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
}
Para la función de instantáneas de fastlane, SnapshotHelper.swift
inicia la aplicación con estos argumentos. Entonces, al interpretar estos valores, esta solución es determinista y pude producir instantáneas correctas para varios idiomas:
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 ""
}
Espero que esto ayude
Quería probar el contenido de las funciones de la interfaz de usuario y no solo su existencia, por lo que no sería adecuado establecer un idioma predeterminado o usar los identificadores de accesibilidad.
Esto se basa en las respuestas de Volodymyr y matsoftware . Sin embargo, sus respuestas se basan en deviceLanguage
que debe establecerse explícitamente en SnapshotHelper
. Esta solución obtiene dinámicamente el idioma real soportado que usa el dispositivo.
- Agregue los archivos
Localizable.strings
a su objetivo de UITest. Agregue el siguiente código a su objetivo de 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 "?" }
Accede al método por
localizedString(key)
Para aquellos idiomas con un código de script, el código de localeCode
será langCode-scriptCode
(por ejemplo, zh-Hans
). De lo contrario, localeCode
será langCode-regionCode
(por ejemplo, pt-BR
). El testBundle
primero intenta resolver el lproj por localeCode
, luego vuelve a solo langCode
.
Si aún no puede obtener el paquete, devuelve "?" para la cadena, por lo que fallará cualquier prueba de IU que busque cadenas específicas.
Si está haciendo esto con el propósito de ejecutar Instantáneas (en lugar de las pruebas de UI reales), creo que la solución más sencilla es hacer trampa y usar HSTestingBackchannel
Es una herramienta que escribí que le permite enviar notificaciones de la clase de UITesting a la aplicación. A continuación, escribe el código en la aplicación que responde directamente a las notificaciones.
¡USTED PUEDE REUTILIZAR LOS PROYECTOS DE LOCALIZACIÓN DE PROYECTOS
Cuando prueba el comportamiento de los cuadros de mensajes, necesita saber exactamente qué cuadro de mensaje acaba de aparecer. Necesita copiar su localización desde otro esquema durante la fase de construcción.
En su objetivo de Pruebas de UI -> Crear fases -> Copiar recursos del paquete, agregue los archivos de localización necesarios (por ejemplo, Localizable.strings).
Agregue una función similar a la siguiente:
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://.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 */
Luego, en tu código puedes usar app.buttons [localizedString ("localized.string.key")]
El artículo completo está aquí: https://github.com/fastlane-old/snapshot/issues/321#issuecomment-159660882