Empezando con Xcode UI testing

0
7061
  1. Introducción
  2. Entorno
  3. Creación de una prueba
  4. Escribir UI test automaticamente
  5. Los frameworks de las pruebas UI
  6. Conclusiones
  7. Referencias

Introducción

Las pruebas de interfaz de usuario (UI tests) son una excelente manera de garantizar que sus interacciones de interfaz de usuario más críticas continúen funcionando a medida que agrega nuevas funciones o refactoriza la base de código de su aplicación.

También es una buena forma de automatizar tareas repetitivas cuando se trabaja con el código de la interfaz de usuario (cuando tienes que navegar profundamente en tu aplicación para probar algo en lo que estás trabajando, por ejemplo).

Escribir y ejecutar pruebas de UI es algo diferente de hacer pruebas unitarias, ya que en realidad estás interactuando con tu aplicación, en lugar de realizar pruebas programáticas contra una determinada API. Ambas tienen mucho valor y el mejor enfoque es utilizar las dos para diferentes tareas.

Xcode se entrega con pruebas de interfaz de usuario integradas en el framework XCTest, que probablemente ya hayas utilizado para pruebas de unidad. Estas funciones de las pruebas UI han existido durante algunos años, pero muchos desarrolladores las han considerado como inestables y difíciles de usar. Y eso solía ser cierto, ya que en las primeras iteraciones las cosas eran realmente muy inestables, pero actualmente creo que merece la pena darles una segunda oportunidad si aún no lo has hecho.

Entorno

Este tutorial está escrito usando el siguiente entorno:

  • Hardware: MacBook Pro 15’ (2,5 GHz Intel Core i7, 16GB DDR3, mediados de 2015)
  • Sistema operativo: macOS Mojave 10.14.2
  • Versiones del software:
    • Xcode: 7+
    • iOS SDK: 9.0+

Creación de una prueba

Si tu aplicación aún no tiene un objetivo («target») de UI test, todo lo que tienes que hacer para agregar uno es ir a File> New> Target … en Xcode y seleccionar «iOS UI Testing Bundle «. Luego, edita el esquema de tu aplicación: ve a «Product»> «Scheme» > «New scheme…» en Xcode y agrega el paquete de pruebas de IU en «Test». Un ejemplo en donde una prueba de UI puede ser una gran solución, es cuando deseamos probar una interfaz de usuario bastante complicada. Digamos que el usuario tiene que pasar por diferentes pantallas y al final de la última pantalla, aparece un botón «Open another flow», que tienes que pulsar para abrir otro flujo:

Entonces, vamos a escribir un test muy simple para comprobar este escenario:

func testExample() {
        //lanzar la aplicación
        app.launch()
        //comprobar que estamos en la primera página
        XCTAssert(app.otherElements["firstView"].exists)
        //obtener todas las paginas de la aplicación
        let tabBarsQuery = app.tabBars
        //pulsar el botón de la segunda barra para cambiar la página
        tabBarsQuery.buttons["Second"].tap()
        //pulsar el botón de la tercera barra para cambiar la página
        tabBarsQuery.buttons["Third"].tap()
        //deslizar a la parte inferior de la segunda página
        app.scrollViews.containing(.staticText, identifier: "Third View").element.swipeUp()
       //pulsar el botón para abrir otro flujo
       app.buttons["Open another flow"].tap()
       //comprobar que la vista de la primera pagina no se muestra
       XCTAssertFalse(app.otherElements["firstView"].exists)
    }

Como se puede ver arriba, realizamos nuestra prueba interactuando con nuestra interfaz de usuario, realizando toques, en lugar de llamar a nuestra propia API. De hecho, nuestras pruebas se ejecutarán en un proceso completamente diferente, por lo que no tenemos acceso a nuestro propio código. Esto nos «obliga» a probar realmente el uso de nuestra aplicación, en lugar de «falsificarla».

Escribir UI test automáticamente

Para empezar a escribir UI tests no hace falta saberse un especial API de Xcode. Podemos crear nuevos tests sin escribir ninguna palabra gracias a una funcionalidad de Xcode. ¡Solo hay que lanzar nuestra aplicación y pulsar un botón! ¡Increíble! Una vez que tu aplicación esté lista para grabar una prueba, abre un fichero en el objetivo («target») de UI test e inserta el cursor en un método de UI test.

Se puede agregar a un método de prueba existente o crear uno nuevo. Haz clic en el botón Grabar («Record») y Xcode inicia su aplicación en Simulator. Cada vez que toca un elemento en la pantalla, Xcode agrega una línea de código a su método de prueba. Haz clic en el botón Grabar («Record») nuevamente para dejar de agregar acciones al método.

Una vez finalizada la prueba, agrega aserciones para verificar si la interfaz de usuario está en el estado correcto. Se puede usar aserciones para probar partes de la interfaz, botones de texto, el número de celdas de vista de tabla, la existencia de un botón en particular y mucho mas.

//comprobar que la vista de la primera pagina no se muestra
XCTAssertFalse(app.otherElements["firstView"].exists)
//comprobar que estamos en la tercera pagina
XCTAssert(app.otherElements["thirdView"].exists)
XCTAssert(app.otherElements["thirdViewLabel"].exists)
Para usar API «otherElements» es imprescindible poner el identificador de accesibilidad

Los frameworks de las pruebas UI

XCUITest

Hace unos años, Apple suspendió su framework de automatización de JavaScript y lo reemplazó con XCUITest. Ésta es una librería nativa compatible con Apple, lo que significa que puedes escribir tus pruebas de UI en Objective-C o Swift.
Una cosa que tengo que destacar es que no comprueba los elementos (etiquetas, botones, etc.) que contienen un valor. En su lugar, verifica el valor que existe en la pantalla.

XCUITest se ejecuta en su propio proceso. Esto tiene algunas ventajas pero también desventajas. Se puede ver todo en la pantalla, pero no puede comprobar cuál es el estado interno de la aplicación. Esto es bastante bueno ya que evita que los desarrolladores cometan errores relacionados con el conocimiento que un usuario normal no tiene. Por otro lado, estar en un proceso separado también significa que necesita algún tiempo para sincronizar el estado de la aplicación. Esto lleva tiempo y ralentiza la prueba. Además, al realizar algunas operaciones que requieren tiempo, puede provocar errores debido a que XCUITest no encuentra el elemento solicitado.

Desde su introducción, se están realizando mejoras constantes con cada lanzamiento de Xcode. La versión más reciente agrega opciones tales como:
iniciar múltiples aplicaciones al mismo tiempo (para mirar cómo interactúan), inicio en caliente (enviar la aplicación al fondo y recuperarla), tomando capturas de pantalla y etc.
Por lo tanto, incluso si decides que XCUITest no es el framework adecuado para tí, es posible que desees realizar un seguimiento del mismo.

EarlGrey

A principios de 2016 Google lanzó EarlGrey. La diferencia entre XCUITest y este framework de automatización de IU es que EarlGrey y la aplicación comparten el mismo proceso. La prueba llamada GreyBox puede influir en la memoria compartida, cambiando así el comportamiento en tiempo de ejecución de la aplicación.
En su repositorio Github, Google proporciona instrucciones detalladas para configurarlo.
En cuanto a la sincronización. Google afirma que debido a que EarlGrey comparte el mismo proceso, se encarga de la sincronización en sí. Aunque puedes tener acceso a él, pero probablemente no lo necesites.

Appium

Appium es diferente en comparación con XCUITest y EarlGrey. Es multiplataforma y no es necesario utilizar un idioma nativo. Entonces, la idea es que tú mismo escribes pruebas en tu idioma preferido y las usas en todas las plataformas compatibles. Esto puede reducir el tiempo de escritura y el mantenimiento de las pruebas a la mitad, ya que, en un mundo ideal, las pruebas funcionan en Android e iOS de la misma manera. Si alguna vez has trabajado en una aplicación que soporta ambas plataformas, ya conoces el resultado. No es realista. En su lugar, Appium escribe las mismas pruebas para ambas plataformas.

Lo que más me echa para atrás al usar Appium es la velocidad. Una simple prueba de inicio de sesión:
1. Iniciar aplicación
2. Introduce credenciales
3. Presione Entrar
4. Comprobar si está conectado
Esto puede tardar con Appium 2(!!!) minutos para completar. Lo mismo se hace en XCUITest o EarlGrey en menos de 10 segundos (lo cual es bastante tiempo).

Cucumberish y otras opciones

Hay más opciones que las anteriores. En caso de que quieras escribir solo en Swift u Objective-C, hay KIF y Frank. De lo contrario, si te gusta BDD y por ejemplo el framework Cucumber, existen frameworks que usan Cucumber, por ejemplo Cucumberish o Calabash. Entre los dos, mi elección preferida es Cucumberish, ya que es un semi-oficial framework para iOS. Está completamente integrado con Xcode Test Navigator y XCUITest, las pruebas están escritas en Swift/Objective-C y no hace falta usar Ruby u otras herramientas para escribir los pasos de las pruebas. Además, los informes de las pruebas aparecerán en Reports Navigator como cualquier prueba de Xcode, pero no tiene buena documentación y se necesita un conocimiento previo de Cucumber.

Lo principal de Cucumberish son sus ficheros «.feature» que contienen las descripciones de las funciones de nuestra aplicación que queremos comprobar con nuestras pruebas. Están escritos en Gherkin, que es el lenguaje que permite describir la funcionalidad de manera natural y sencilla.

Feature: As someone who plans to automate the iOS project test cases, I will use Cucumberish.

# primero en el escenario es su nombre, 
# que también aparecerá en el Xcode Test Navigator
Scenario: First scenario

# esto es el primer paso del escenario
# el contexto de nuestra prueba
Given user launch the application
# la condición de nuestra prueba
When user touch the buttons
# el resultado que queremos conseguir
Then user sees other flow

# por favor, ten en cuenta que cada paso tiene que empezar 
# con "Given", "When", "Then", "And", or "But".
When I run the application
Then I can see the first page 

Luego, el desarrollador tiene que correlacionar los pasos del escenario con las pruebas de XCUITest ya escritas. Así, las personas no-técnicas y los desarrolladores pueden colaborar en el mismo proyecto con el mínimo esfuerzo.

import XCTest

//la interconexión entre las pruebas de desarrollador
//y las descripciones en el fichero .feature
@objc class SampleUITestSteps: SampleUITests {

    func SampleUITestsImplementation(){
        Given("user launch the application") { (args, userInfo) in
            SampleUITests().runApplication()
        }

        Then("verify that we are at the start") { (_, _) in
            SampleUITestSteps().verifyThatWeAreAtTheStart()
        }

        When("user touch the buttons") { (args, userInfo) in
            SampleUITests().openOtherFlow()
        }

        Then("user sees other flow") { (args, userInfo) -> Void in
            SampleUITests().verifyOtherFlowIsOpen();
        }

        When("I run the application") { (args, userInfo) in
            SampleUITests().runApplication()
        }

        Then("I can see the first page") { (args, userInfo) in
            SampleUITests().verifyThatWeAreAtTheStart()
        }
    }
}

De esta manera, el desarrollador puede adaptar sus pruebas ya escritas con los requisitos de cliente o los criterios de aceptación de la historia de usuario.

import XCTest

//las pruebas de XCUITest ya escritas por el desarrollador 
class SampleUITests: XCTestCase {
    let application = XCUIApplication()

    func runApplication(){
        //lanzar la aplicación
        application.launch()
    }

    func openOtherFlow() {
        //obtener todas paginas de la aplicación
        let tabBarsQuery = application.tabBars
        //pulsar el botón de la segunda barra para cambiar la página
        tabBarsQuery.buttons["Second"].tap()
        //pulsar el botón de la tercera barra para cambiar la página
        tabBarsQuery.buttons["Third"].tap()
        //deslizar a la parte inferior de la segunda página
        application.scrollViews.containing(.staticText, identifier:"thirdViewLabel").element.swipeUp()
        //pulsar el botón para abrir otro flujo
        application.buttons["Open another flow"].tap()
    }

    func verifyThatWeAreAtTheStart() {
        //comprobar que estamos en la primera página
        XCTAssert(application.otherElements["firstView"].exists)
    }

    func verifyOtherFlowIsOpen() {
        //comprobar que las vistas de las tabs no se muestran
        XCTAssertFalse(application.otherElements["firstView"].exists)
        XCTAssertFalse(application.otherElements["secondView"].exists)
        XCTAssertFalse(application.otherElements["thirdView"].exists)
    }

    func tapBarButtons() {
        let tabBarsQuery = XCUIApplication().tabBars
        let secondButton = tabBarsQuery.buttons["Second"]
        secondButton.tap()
        tabBarsQuery.buttons["First"].tap()
        secondButton.tap()
        tabBarsQuery.buttons["Third"].tap()
    }
}

Conclusiones

Con el fin de que tus pruebas de UI sean fáciles de mantener y rápidas de ejecutar, es recomendable hacerlas lo más simples posible y dejar la verificación lógica compleja a pruebas unitarias. Este tipo de pruebas ofrece la mejor «inversión», ya que las pruebas de UI son más lentas, puesto que tienen que ejecutar tu aplicación y esperar animaciones, etc. Además, las pruebas UI hacen que la depuración sea mucho menos dolorosa y evitan los problemas que no son tan evidentes desde el punto de vista del código. Como cuando tienes los elementos invisibles o inaccesibles en tu interfaz.

En general, lo más importante de las pruebas y esto también se aplica a las pruebas de UI, es que me da mucha más confianza al cambiar mi aplicación o antes de enviarlo al cliente.

Como se puede ver en los ejemplos, las pruebas de interfaz de usuario también usan las funciones de accesibilidad de UIKit para encontrar los elementos de interfaz, lo que significa que si pasas algún tiempo implementando algunas pruebas de interfaz de usuario, puedes mejorar la accesibilidad de tu aplicación. ¡Bingo!

Referencias

 

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad