Prueba de interfaz de usuario Xcode 7: cómo descartar una serie de alertas del sistema en código

Estoy escribiendo casos de testing de UI usando la nueva function de testing de UI de Xcode 7. En algún momento de mi aplicación, le pido permiso al usuario para acceder a la camera y enviar notifications. Entonces aparecerán dos windows emergentes de iOS: "MyApp Would Like to Access the Camera" y "MyApp Would Like to Send You Notifications" . Me gustaría que mi testing excluya las windows emergentes.

La grabación de UI generó el siguiente código para mí:

 [app.alerts[@"cameraAccessTitle"].collectionViews.buttons[@"OK"] tap]; 

Sin embargo, [app.alerts[@"cameraAccessTitle"] exists] resuelve en falso y el código anterior genera un error: Error de Assertion Failure: UI Testing Failure - Failure getting refresh snapshot Error Domain=XCTestManagerErrorDomain Code=13 "Error copying attributes -25202" : Error de Assertion Failure: UI Testing Failure - Failure getting refresh snapshot Error Domain=XCTestManagerErrorDomain Code=13 "Error copying attributes -25202"

Entonces, ¿cuál es la mejor manera de descartar una stack de alertas del sistema en la testing? Las windows emergentes del sistema interrumpen el flujo de mi aplicación y fallan de inmediato en mis casos normales de testing de IU. De hecho, se agradecen las recomendaciones sobre cómo puedo evitar las alertas del sistema para que pueda reanudar las testings del flujo habitual.

Esta pregunta podría estar relacionada con esta publicación SO que tampoco tiene una respuesta: Xcode7 | Xcode UI Tests | ¿Cómo manejar la alerta de service de location?

Gracias por adelantado.

Xcode 7.1

Xcode 7.1 finalmente solucionó el problema con las alertas del sistema. Hay, sin embargo, dos pequeñas gotchas.

Primero, debe configurar un "Controlador de intercepción de interfaz de usuario" antes de presentar la alerta. Esta es nuestra manera de decirle al marco cómo manejar una alerta cuando aparece.

Segundo, luego de presentar la alerta, debe interactuar con la interfaz. Simplemente tocar la aplicación funciona bien, pero es obligatorio.

 addUIInterruptionMonitorWithDescription("Location Dialog") { (alert) -> Bool in alert.buttons["Allow"].tap() return true } app.buttons["Request Location"].tap() app.tap() // need to interact with the app for the handler to fire 

El "Cuadro de dialog de location" es solo una cadena para ayudar al desarrollador a identificar a qué manejador se accedió, no es específico para el tipo de alerta.

Creo que volver al true desde el manejador lo marca como "completo", lo que significa que no se volverá a llamar. Para su situación, intente devolver false para que la segunda alerta active de nuevo el manejador.

Xcode 7.0

Lo siguiente rechazará una sola "alerta del sistema" en Xcode 7 Beta 6:

 let app = XCUIApplication() app.launch() // trigger location permission dialog app.alerts.element.collectionViews.buttons["Allow"].tap() 

Beta 6 introdujo un montón de correcciones para las testings de interfaz de usuario y creo que esta fue una de ellas.

También tenga en count que estoy llamando -element directamente en -alerts . Llamar a -element en un XCUIElementQuery obliga al marco a elegir el elemento coincidente "one and only" en la pantalla. Esto funciona muy bien para las alertas en las que solo puede tener una visible a la vez. Sin embargo, si intenta esto con una label y tiene dos tags, el marco generará una exception.

Dios mío Siempre toca "Do not Allow", aunque deliberadamente doy un toque en "Permitir"

Al less

 ` if app.alerts.element.collectionViews.buttons["Allow"].exists { app.tap() }` 

me permite avanzar y hacer otras testings.

¡Dios! Odio cómo XCTest tiene el peor time para lidiar con UIView Alerts. Tengo una aplicación donde recibo 2 alertas, la primera quiere que select "Permitir" para habilitar los services de location para los permissions de la aplicación, luego, en una página de bienvenida, el usuario debe presionar un button UIB llamado "Activar location" y finalmente hay un alerta de SMS de notificación en un UIViewAlert y el usuario debe seleccionar "Aceptar". El problema que teníamos era no poder interactuar con las Alertas del sistema, sino también una condición de carrera en la que el comportamiento y su apariencia en la pantalla era inoportuna. Parece que si usa alert.element.buttons["whateverText"].tap la lógica de XCTest es seguir presionando hasta que se agote el time de la testing. Entonces básicamente sigue presionando cualquier cosa en la pantalla hasta que todas las alertas del sistema estén despejadas.

Este es un truco, pero esto es lo que funcionó para mí.

  func testGetPastTheStupidAlerts(){ let app = XCUIApplication() app.launch() if app.alerts.element.collectionViews.buttons["Allow"].exists { app.tap() } app.buttons["TURN ON MY LOCATION"].tap() } 

La cadena "Permitir" se ignora por completo y la lógica de app.tap() se denomina evreytime a la vista está visible y finalmente el button que quería alcanzar ["Activar location"] es accesible y el pase de testing

~ Totalmente confundido, gracias Apple.

C objective

 -(void) registerHandlerforDescription: (NSString*) description { [self addUIInterruptionMonitorWithDescription:description handler:^BOOL(XCUIElement * _Nonnull interruptingElement) { XCUIElement *element = interruptingElement; XCUIElement *allow = element.buttons[@"Allow"]; XCUIElement *ok = element.buttons[@"OK"]; if ([ok exists]) { [ok tap]; return YES; } if ([allow exists]) { [allow tap]; return YES; } return NO; }]; } -(void)setUp { [super setUp]; self.continueAfterFailure = NO; self.app = [[XCUIApplication alloc] init]; [self.app launch]; [self registerHandlerforDescription:@"“MyApp” would like to make data available to nearby Bluetooth devices even when you're not using app."]; [self registerHandlerforDescription:@"“MyApp” Would Like to Access Your Photos"]; [self registerHandlerforDescription:@"“MyApp” Would Like to Access the Camera"]; } 

Rápido

 addUIInterruptionMonitorWithDescription("Description") { (alert) -> Bool in alert.buttons["Allow"].tap() alert.buttons["OK"].tap() return true } 

Lo único que logré resolver de manera confiable fue configurar dos testings separadas para manejar las alertas. En la primera testing, llamo a app.tap() y no hago nada más. En la segunda testing, vuelvo a llamar a app.tap() y luego hago el trabajo real.

Suena como el enfoque para implementar el acceso a la camera y las notifications se insertan como dices, pero no se gestionan físicamente y se dejan random cuando y cómo se muestran.

Sospecho que uno es activado por el otro y cuando se hace clic programáticamente, también borra el otro (lo que Apple probablemente nunca permitirá)

Piensa en eso, ¿estás pidiendo permiso a los usuarios para luego tomar la decisión en su nombre? ¿Por qué? Porque quizás no puedas trabajar con tu código.

Cómo arreglar: rastrear dónde estos dos componentes activan los dialogs emergentes, dónde se llaman ?, reescribir para disparar solo uno, enviar una NSNotificación cuando se ha completado un dialog para disparar y mostrar el otro.

Desalentaría seriamente el enfoque de hacer clic en los botones de dialog progtwigdos para el usuario.