Cómo ajustar tus pruebas con dispositivos administrados por Gradle

Los dispositivos administrados por Gradle mejoran la coherencia, el rendimiento y la confiabilidad de tus pruebas instrumentadas automatizadas. Esta función, disponible para el nivel de API 27 y versiones posteriores, te permite configurar dispositivos de prueba físicos virtuales o remotos en los archivos Gradle de tu proyecto. El sistema de compilación usa las configuraciones para administrar por completo (es decir, crear, implementar y eliminar) esos dispositivos cuando ejecutas tus pruebas automatizadas.

Esta función otorga visibilidad a Gradle no solo de las pruebas que estás ejecutando, sino también del ciclo de vida de los dispositivos, lo que mejora la calidad de tu experiencia con las pruebas de las siguientes maneras:

  • Controla problemas relacionados con el dispositivo para garantizar que se ejecuten las pruebas.
  • En el caso de los dispositivos virtuales, usa instantáneas del emulador para mejorar el tiempo de inicio y el uso de memoria del dispositivo, y para restablecer los dispositivos a un estado limpio entre las pruebas.
  • Almacena en caché los resultados de la prueba y vuelve a ejecutar solo las pruebas que puedan proporcionar resultados diferentes.
  • Proporciona un entorno coherente para ejecutar tus pruebas entre ejecuciones locales y remotas.

Cómo crear un dispositivo virtual administrado por Gradle

Puedes especificar un dispositivo virtual que quieras que Gradle use para probar tu app en el archivo de compilación de nivel de módulo. En la siguiente muestra de código, se crea un Pixel 2 que ejecuta el nivel de API 30 como un dispositivo administrado por Gradle.

Kotlin

android {
  testOptions {
    managedDevices {
      localDevices {
        create("pixel2api30") {
          // Use device profiles you typically see in Android Studio.
          device = "Pixel 2"
          // Use only API levels 27 and higher.
          apiLevel = 30
          // To include Google services, use "google".
          systemImageSource = "aosp"
        }
      }
    }
  }
}

Groovy

android {
  testOptions {
    managedDevices {
      localDevices {
        pixel2api30 {
          // Use device profiles you typically see in Android Studio.
          device = "Pixel 2"
          // Use only API levels 27 and higher.
          apiLevel = 30
          // To include Google services, use "google".
          systemImageSource = "aosp"
        }
      }
    }
  }
}

Cómo definir grupos de dispositivos

Para facilitar el ajuste de pruebas en varias configuraciones de dispositivos, como diferentes niveles de API y factores de forma, puedes definir varios dispositivos administrados por Gradle y agregarlos a un grupo con nombre. Luego, Gradle puede ejecutar tus pruebas en todos los dispositivos del grupo de manera simultánea.

En el siguiente ejemplo, se muestran dos dispositivos que se agregaron a un grupo de dispositivos llamado phoneAndTablet.

Kotlin

testOptions {
  managedDevices {
    localDevices {
      create("pixel2api29") { ... }
      create("nexus9api30") { ... }
    }
    groups {
      create("phoneAndTablet") {
        targetDevices.add(devices["pixel2api29"])
        targetDevices.add(devices["nexus9api30"])
      }
    }
  }
}

Groovy

testOptions {
  managedDevices {
    localDevices {
      pixel2api29 { ... }
      nexus9api30 { ... }
    }
    groups {
      phoneAndTablet {
        targetDevices.add(devices.pixel2api29)
        targetDevices.add(devices.nexus9api30)
      }
    }
  }
}

Cómo ejecutar las pruebas

Para que los dispositivos administrados por Gradle que configuraste ejecuten tus pruebas, usa el siguiente comando. device-name es el nombre del dispositivo que configuraste en la secuencia de comandos de compilación de Gradle (como pixel2api30) y BuildVariant es la variante de compilación de la app que quieres probar.

En Windows:

gradlew device-nameBuildVariantAndroidTest

En Linux o macOS:

./gradlew device-nameBuildVariantAndroidTest

Para ejecutar las pruebas en un grupo de dispositivos administrados por Gradle, usa los siguientes comandos.

En Windows:

gradlew group-nameGroupBuildVariantAndroidTest

En Linux o macOS:

./gradlew group-nameGroupBuildVariantAndroidTest

El resultado de la prueba incluye una ruta de acceso a un archivo HTML con el informe de la prueba. También puedes importar los resultados de la prueba a Android Studio para analizarlos en detalle. Para ello, haz clic en Run > Test History en el IDE.

Cómo habilitar la fragmentación de pruebas

Los dispositivos administrados por Gradle admiten la fragmentación de pruebas, que te permite dividir tu paquete de pruebas en una serie de instancias de dispositivos virtuales, denominadas fragmentos, que se ejecutan en forma simultánea. El uso de la fragmentación de pruebas puede ser útil para reducir el tiempo general de ejecución de pruebas a costa de recursos de procesamiento adicionales.

Para establecer la cantidad de fragmentos que quieres usar en la ejecución de una prueba determinada, configura lo siguiente en tu archivo gradle.properties:

android.experimental.androidTest.numManagedDeviceShards=<number_of_shards>

Cuando ejecutas tus pruebas con esta opción, los dispositivos administrados por Gradle aprovisionan la cantidad de fragmentos que especificas para cada perfil de dispositivo en la ejecución de la prueba. Por ejemplo, si implementaste las pruebas en un grupo de tres dispositivos y configuraste numManagedDeviceShards en dos, los dispositivos administrados por Gradle aprovisionarán un total de seis dispositivos virtuales para la ejecución de la prueba.

Cuando se completan tus pruebas, Gradle genera los resultados de la prueba en un archivo .proto para cada fragmento que se usa en la ejecución de la prueba.

Cómo usar dispositivos de prueba automatizados

Los dispositivos administrados por Gradle admiten un tipo de dispositivo emulador, llamado dispositivo de prueba automatizado (ATD), que está optimizado para reducir los recursos de CPU y memoria al ejecutar pruebas instrumentadas. Los ATD mejoran el rendimiento del tiempo de ejecución de las siguientes maneras:

  • Quitan las apps preinstaladas que comúnmente no resultan útiles para probar tu app.
  • Inhabilitan ciertos servicios en segundo plano que comúnmente no son útiles para probar tu app.
  • Inhabilitan la renderización del hardware.

Antes de comenzar, no olvides actualizar Android Emulator a la versión más reciente disponible. Luego, especifica una imagen "-atd" al definir un dispositivo administrado por Gradle en el archivo de compilación de nivel de módulo, como se muestra a continuación:

Kotlin

android {
  testOptions {
    managedDevices {
      localDevices {
        create("pixel2api30") {
          // Use device profiles you typically see in Android Studio.
          device = "Pixel 2"
          // ATDs currently support only API level 30.
          apiLevel = 30
          // You can also specify "google-atd" if you require Google Play Services.
          systemImageSource = "aosp-atd"
        }
      }
    }
  }
}

Groovy

android {
  testOptions {
    managedDevices {
      localDevices {
        pixel2api30 {
          // Use device profiles you typically see in Android Studio.
          device = "Pixel 2"
          // ATDs currently support only API level 30.
          apiLevel = 30
          // You can also specify "google-atd" if you require Google Play Services.
          systemImageSource = "aosp-atd"
        }
      }
    }
  }
}

También puedes crear grupos de dispositivos como lo haces con otros dispositivos administrados por Gradle. Para aprovechar todavía más las mejoras de rendimiento, también puedes usar ATD con fragmentación de pruebas para reducir el tiempo total de ejecución del paquete de pruebas.

¿Qué se quita de las imágenes de ATD?

Además de operar en un modo sin interfaz gráfica, los ATDs también optimizan el rendimiento al inhabilitar o eliminar las apps y los servicios que, por lo general, no son necesarios para probar el código de tu app. En la siguiente tabla, se proporciona una descripción general de los componentes que quitamos o inhabilitamos en las imágenes de ATD y las descripciones de los motivos por los que podrían no ser útiles.

Qué se quita de las imágenes de ATD Por qué es posible que no lo necesites al ejecutar pruebas automatizadas
Apps de productos de Google:
  • Correo electrónico
  • Maps
  • Chrome
  • Mensajes
  • Play Store y otras
Tus pruebas automatizadas deben enfocarse en la lógica de tu propia app y suponer que otras apps o la plataforma funcionarán correctamente.

Con Espresso-Intents, puedes hacer coincidir y validar los intents salientes o incluso proporcionar respuestas de stub en lugar de respuestas de intents reales.

Configuración de apps y servicios:
  • CarrierConfig
  • EmergencyInfo
  • OneTimeInitializer
  • PhotoTable (protectores de pantalla)
  • Aprovisionamiento
  • App de Configuración
  • StorageManager
  • Configuración del APN de telefonía
  • WallpaperCropper
  • WallpaperPicker
Estas apps presentan una GUI para que los usuarios finales puedan cambiar la configuración de la plataforma, realizar ajustes en su dispositivo o administrar su almacenamiento. Esto suele estar fuera del alcance de las pruebas automatizadas a nivel de la app.


Nota: El proveedor de configuración todavía está disponible en la imagen de ATD.

IU del sistema Tus pruebas automatizadas deben enfocarse en la lógica de tu propia app y suponer que otras apps o la plataforma funcionarán correctamente.
Apps y servicios del AOSP:
  • Browser2
  • Calendario
  • Camera2
  • Contactos
  • Marcador
  • DeskClock
  • Gallery2
  • LatinIME
  • Launcher3QuickStep
  • Música
  • QuickSearchBox
  • SettingsIntelligence
Estas apps y servicios suelen estar fuera del alcance de las pruebas automatizadas del código de tu app.

Cómo usar dispositivos de Firebase Test Lab

Puedes ejecutar tus pruebas instrumentadas automatizadas a gran escala en dispositivos de Firebase Test Lab cuando usas dispositivos administrados por Gradle. Test Lab te permite ejecutar las pruebas simultáneamente en una amplia variedad de dispositivos Android, tanto físicos como virtuales. Estas pruebas se ejecutan en centros de datos remotos de Google. Gracias a la compatibilidad de los dispositivos administrados por Gradle, el sistema de compilación puede administrar por completo las pruebas en ejecución en estos dispositivos de Test Lab según tu configuración.

Primeros pasos

En los siguientes pasos, se describe cómo comenzar a usar dispositivos de Firebase Test Lab con dispositivos administrados por Gradle. Ten en cuenta que estos pasos usan gcloud CLI para proporcionar credenciales de usuario, que podrían no aplicarse a todos los entornos de desarrollo. Para obtener más información sobre qué proceso de autenticación usar para tus necesidades, consulta Cómo funcionan las credenciales predeterminadas de la aplicación.

  1. Para crear un proyecto de Firebase, ve a Firebase console. Haz clic en Add project y sigue las indicaciones que aparecen en pantalla para crear un proyecto. Recuerda tu ID del proyecto.

  2. Para instalar Google Cloud CLI, sigue los pasos que se indican en la página sobre cómo instalar gcloud CLI.

  3. Configura tu entorno local.

    1. Vincula a tu proyecto de Firebase en gcloud:

      gcloud config set project FIREBASE_PROJECT_ID
      
    2. Autoriza el uso de tus credenciales de usuario para el acceso a la API. Te recomendamos que la autorices pasando un archivo JSON de cuenta de servicio a Gradle con la DSL en la secuencia de comandos de compilación a nivel de módulo:

      Kotlin

      firebaseTestLab {
        ...
        serviceAccountCredentials.set(file(SERVICE_ACCOUNT_JSON_FILE))
      }
      

      Groovy

      firebaseTestLab {
        ...
        serviceAccountCredentials = file(SERVICE_ACCOUNT_JSON_FILE)
      }
      

      Como alternativa, puedes autorizarlo de forma manual con el siguiente comando de la terminal:

      gcloud auth application-default login
      
    3. Opcional: Agrega tu proyecto de Firebase como el proyecto de cuota. Este paso solo es necesario si superas la cuota sin costo de Test Lab.

      gcloud auth application-default set-quota-project FIREBASE_PROJECT_ID
      
  4. Habilita las APIs obligatorias.

    En la página Biblioteca de APIs de Google Developers Console, habilita la API de Cloud Testing y la API de Cloud Tool Results. Para ello, escribe los nombres de estas APIs en el cuadro de búsqueda de la parte superior de la consola y, luego, haz clic en Enable API en la página de resumen de cada API.

  5. Configura tu proyecto de Android.

    1. Agrega el complemento de Firebase Test Lab a la secuencia de comandos de compilación de nivel superior:

      Kotlin

      plugins {
        ...
        id("com.google.firebase.testlab") version "0.0.1-alpha05" apply false
      }
      

      Groovy

      plugins {
        ...
        id 'com.google.firebase.testlab' version '0.0.1-alpha05' apply false
      }
      
    2. Habilita tipos de dispositivos personalizados en el archivo gradle.properties:

      android.experimental.testOptions.managedDevices.customDevice=true
      
    3. Agrega el complemento de Firebase Test Lab a la secuencia de comandos de compilación a nivel de módulo:

      Kotlin

      plugins {
       ...
       id "com.google.firebase.testlab"
      }
      

      Groovy

      plugins {
       ...
       id 'com.google.firebase.testlab'
      }
      

Cómo especificar un dispositivo de Test Lab

Puedes especificar un dispositivo de Firebase Test Lab para que Gradle pruebe tu app en la secuencia de comandos de compilación a nivel de módulo. En la siguiente muestra de código, se crea un Pixel 3 que ejecuta el nivel de API 30 como un dispositivo de Test Lab administrado por Gradle, llamado ftlDevice. El bloque firebaseTestLab {} está disponible cuando aplicas el complemento com.google.firebase.testlab a tu módulo.

Kotlin

firebaseTestLab {
  managedDevices {
    create("ftlDevice") {
      device = "Pixel3"
      apiLevel = 30
    }
  }
  ...
}

Groovy

firebaseTestLab {
  managedDevices {
    ftlDevice {
      device = "Pixel3"
      apiLevel = 30
    }
  }
  ...
}

Para definir un grupo de dispositivos administrados por Gradle, incluidos los dispositivos de Firebase Test Lab, consulta Cómo definir grupos de dispositivos.

Para ejecutar las pruebas, usa los mismos comandos que se usan en otros dispositivos administrados por Gradle. Ten en cuenta que Gradle no ejecuta pruebas de manera simultánea ni admite otras configuraciones de Google Cloud CLI para dispositivos de Test Lab.

Cómo optimizar las ejecuciones de pruebas con la fragmentación inteligente

Las pruebas de dispositivos de Test Lab administrados por Gradle admiten la fragmentación inteligente. La fragmentación inteligente distribuye en forma automática tus pruebas entre fragmentos de manera que cada uno se ejecute aproximadamente por el mismo tiempo, lo que reduce los esfuerzos de asignación manual y la duración general de la ejecución de prueba. La fragmentación inteligente usa tu historial de pruebas o la información sobre cuánto tiempo tardaron en ejecutarse tus pruebas, para distribuirlas de manera óptima. Ten en cuenta que necesitas la versión 0.0.1-alpha05 del complemento de Gradle para que Firebase Test Lab use la fragmentación inteligente.

Para habilitar la fragmentación inteligente, especifica el tiempo que deberían tardar las pruebas dentro de cada fragmento. Debes establecer la duración objetivo de los fragmentos en, al menos, cinco minutos menos que timeoutMinutes para evitar la situación en la que se cancelen los fragmentos antes de que puedan finalizar las pruebas.

firebaseTestLab {
  ...
  testOptions {
    targetedShardDurationMinutes = 2
  }
}

Para obtener más información, lee sobre las opciones de DSL de dispositivos de Firebase Test Lab.

DSL actualizada para dispositivos de Test Lab

Hay más opciones de DSL que puedes configurar para personalizar las ejecuciones de prueba o migrar desde otras soluciones que quizás ya estés usando. Consulta algunas de estas opciones, como se describe en el siguiente fragmento de código.

firebaseTestLab {
  ...

  /**
   * A path to a JSON file that contains service account credentials to access to
   * a Firebase Test Lab project.
   */
  serviceAccountCredentials.set(file("your_service_account_credentials.json"))


  testOptions {
    fixture {
      /**
       * Whether to grant permissions on the device before tests begin.
       * Available options are "all" or "none".
       *
       * Default value is "all".
       */
      grantedPermissions = "all"

      /**
       * Map of files to push to the device before starting the test.
       *
       * The key is the location on the device.
       * The value is the location of the file, either local or in Google Cloud.
       */
      extraDeviceFiles["/sdcard/dir1/file1.txt"] = "local/file.txt"
      extraDeviceFiles["/sdcard/dir2/file2.txt"] = "gs://bucket/file.jpg"

      /**
       * The name of the network traffic profile.
       *
       * Specifies network conditions to emulate when running tests.
       *
       * Default value is empty.
       */
      networkProfile = "LTE"
    }

    execution {
      /**
       * The maximum time to run the test execution before cancellation,
       * measured in minutes. Does not include the setup or teardown of device,
       * and is handled server-side.
       *
       * The maximum possible testing time is 45 minutes on physical devices
       * and 60 minutes on virtual devices.
       *
       * Defaults to 15 minutes.
       */
       timeoutMinutes = 30

      /**
       * Number of times the test should be rerun if tests fail.
       * The number of times a test execution should be retried if one
       * or more of its test cases fail.
       *
       * The max number of times is 10.
       *
       * The default number of times is 0.
       */
      maxTestReruns = 2

      /**
       * Ensures only a single attempt is made for each execution if
       * an infrastructure issue occurs. This doesn't affect `maxTestReruns`.
       * Normally, two or more attempts are made by Firebase Test Lab if a
       * potential infrastructure issue is detected. This is best enabled for
       * latency sensitive workloads. The number of execution failures might be
       * significantly greater with `failFast` enabled.
       *
       * Defaults to false.
       */
      failFast = false

      /**
       * The number of shards to split the tests across.
       *
       * Default to 0 for no sharding.
       */
      numUniformShards = 20
    }

    /**
     * For smart sharding, the target length of time each shard should takes in
     * minutes. Maxes out at 50 shards for physical devices and 100 shards for
     * virtual devices.
     *
     * Only one of numUniformShards or targetedShardDurationMinutes can be set.
     *
     * Defaults to 0 for no smart sharding.
     */
     targetedShardDurationMinutes = 15
    }

    results {
      /**
       * The name of the Google storage bucket to store the test results in.
       *
       * If left unspecified, the default bucket is used.
       *
       * Please refer to Firebase Test Lab permissions for required permissions
       * for using the bucket.
       */
      cloudStorageBucket = "bucketLocationName"

      /**
       * Name of test results for the Firebase console history list.
       * All tests results with the same history name are grouped
       * together in the Firebase console in a time-ordered test history list.
       *
       * Defaults to the application label in the APK manifest in Flank/Fladle.
       */
      resultsHistoryName = "application-history"

      /**
       * List of paths to copy from the test device's storage to the test
       * results folder. These must be absolute paths under /sdcard or
       * /data/local/tmp.
       */
      directoriesToPull.addAll(
        "/sdcard/path/to/something"
      )

      /**
       * Whether to enable video recording during the test.
       *
       * Disabled by default.
       */
      recordVideo = false

      /**
       * Whether to enable performance metrics. If enabled, monitors and records
       * performance metrics such as CPU, memory, and network usage.
       *
       * Defaults to false.
       */
      performanceMetrics = true
  }
}