Google is committed to advancing racial equity for Black communities. See how.
Se usó la API de Cloud Translation para traducir esta página.
Switch to English

Ejemplo de prueba de TF de extremo a extremo

Este tutorial lo guía a través de la creación de una configuración de prueba de la Federación de Comercio (TF) "hola mundo" y le brinda una introducción práctica al marco TF. A partir de un entorno de desarrollo, creará una configuración simple y agregará funciones.

El tutorial presenta el proceso de desarrollo de la prueba como un conjunto de ejercicios, cada uno de los cuales consta de varios pasos, que demuestran cómo construir y refinar gradualmente su configuración. Se proporciona todo el código de muestra que necesita para completar la configuración de la prueba, y el título de cada ejercicio está anotado con una letra que describe los roles involucrados en ese paso:

  • D para desarrollador
  • Yo para integrador
  • R para Test Runner

Después de completar el tutorial, tendrá una configuración TF en funcionamiento y comprenderá muchos conceptos importantes en el marco TF.

Configuración de la Federación de Comercio

Para obtener detalles sobre cómo configurar el entorno de desarrollo TF, consulte Configuración de la máquina . El resto de este tutorial asume que tiene un shell abierto que se ha inicializado en el entorno TF.

Para simplificar, este tutorial ilustra cómo agregar una configuración y sus clases a la biblioteca principal del marco TF. Esto puede extenderse al desarrollo de módulos fuera del árbol de fuentes compilando el JAR comercializado y luego compilando sus módulos con ese JAR.

Creando una clase de prueba (D)

Creemos una prueba de hola mundo que simplemente arroje un mensaje a stdout. Una prueba de intercambio generalmente implementa la interfaz IRemoteTest . Aquí hay una implementación para HelloWorldTest:

package com.android.tradefed.example;

import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.invoker.TestInformation;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.testtype.IRemoteTest;

public class HelloWorldTest implements IRemoteTest {
    @Override
    public void run(TestInformation testInfo, ITestInvocationListener listener) throws DeviceNotAvailableException {
        CLog.i("Hello, TF World!");
    }
}

Guarde este código de muestra en <tree>/tools/tradefederation/core/src/com/android/tradefed/example/HelloWorldTest.java y reconstruya tradefed desde su shell:

m -jN

Tenga en cuenta que CLog.i en el ejemplo anterior se usa para dirigir la salida a la consola. Se describe más información sobre el registro en la Federación de Comercio en Registro (D, I, R) .

Si la compilación no tiene éxito, consulte Configuración de la máquina para asegurarse de que no se perdió ningún paso.

Creando una configuración (I)

Las pruebas de Trade Federation se hacen ejecutables mediante la creación de una configuración , un archivo XML que indica a tradefed qué prueba (o pruebas) ejecutar, así como qué otros módulos ejecutar y en qué orden.

Creemos una nueva configuración para nuestro HelloWorldTest (tenga en cuenta el nombre completo de la clase de HelloWorldTest):

<configuration description="Runs the hello world test">
    <test class="com.android.tradefed.example.HelloWorldTest" />
</configuration>

Guarde estos datos en un archivo helloworld.xml cualquier lugar de su sistema de archivos local (por ejemplo, /tmp/helloworld.xml ). TF analizará el archivo XML de configuración (también conocido como config ), cargará la clase especificada mediante la reflexión, la instanciará, la IRemoteTest a un IRemoteTest y llamará a su método de run .

Ejecutando la configuración (R)

Desde su shell, inicie la consola tradefed:

tradefed.sh

Asegúrese de que un dispositivo esté conectado a la máquina host y sea visible para intercambiar:

tf> list devices
Serial            State      Product   Variant   Build   Battery
004ad9880810a548  Available  mako      mako      JDQ39   100

Las configuraciones se pueden ejecutar utilizando el comando de consola run <config> . Tratar:

tf> run /tmp/helloworld.xml
05-12 13:19:36 I/TestInvocation: Starting invocation for target stub on build 0 on device 004ad9880810a548
Hello, TF World!

Debería ver "¡Hola, TF World!" salida en el terminal.

Puede confirmar que un comando ha terminado de ejecutarse utilizando list invocations o li en el indicador de la consola, y no debería imprimir nada. Si los comandos se están ejecutando actualmente, se muestran de la siguiente manera:

tf >l i
Command Id  Exec Time  Device       State
10          0m:00      [876X00GNG]  running stub on build(s) 'BuildInfo{bid=0, target=stub, serial=876X00GNG}'

Agregar la configuración a la ruta de clases (D, I, R)

Para mayor comodidad de implementación, también puede agrupar configuraciones en los JAR intercambiados. Tradefed reconoce automáticamente todas las configuraciones colocadas en carpetas de configuración en la ruta de clases.

Para ilustrarlo, mueva el archivo helloworld.xml a la biblioteca central de tradefed ( <tree>/tools/tradefederation/core/res/config/example/helloworld.xml ). Reconstruir tradefed, reiniciar la consola tradefed, luego pedir a tradefed que muestre la lista de configuraciones de la ruta de clases:

tf> list configs
[…]
example/helloworld: Runs the hello world test

Ahora puede ejecutar la configuración de helloworld usando:

tf> run example/helloworld
05-12 13:21:21 I/TestInvocation: Starting invocation for target stub on build 0 on device 004ad9880810a548
Hello, TF World!

Interactuar con un dispositivo (D, R)

Hasta ahora, nuestro HelloWorldTest no está haciendo nada interesante. La especialidad de Tradefed es ejecutar pruebas con dispositivos Android, así que agreguemos un dispositivo Android a la prueba.

Las pruebas pueden obtener una referencia a un dispositivo Android mediante TestInformation , proporcionada por el marco cuando se llama al método de IRemoteTest#run .

Modifiquemos el mensaje de impresión HelloWorldTest para mostrar el número de serie del dispositivo:

@Override
public void run(TestInformation testInfo, ITestInvocationListener listener) throws DeviceNotAvailableException {
    CLog.i("Hello, TF World! I have device " + testInfo.getDevice().getSerialNumber());
}

Ahora reconstruya Tradefed y verifique la lista de dispositivos:

tradefed.sh
tf> list devices
Serial            State      Product   Variant   Build   Battery
004ad9880810a548  Available  mako      mako      JDQ39   100

Tome nota del número de serie que figura como Disponible ; ese es el dispositivo que debe asignarse a HelloWorld:

tf> run example/helloworld
05-12 13:26:18 I/TestInvocation: Starting invocation for target stub on build 0 on device 004ad9880810a548
Hello, TF World! I have device 004ad9880810a548

Debería ver el nuevo mensaje de impresión mostrando el número de serie del dispositivo.

Envío de resultados de pruebas (D)

IRemoteTest informa los resultados llamando a métodos en la instancia ITestInvocationListener proporcionada al método #run . El marco TF en sí mismo es responsable de informar el inicio (a través de ITestInvocationListener # invocationStarted ) y el final (a través de ITestInvocationListener # invocationEnded ) de cada invocación.

Una ejecución de prueba es una colección lógica de pruebas. Para informar los resultados de la prueba, IRemoteTest es responsable de informar el inicio de una ejecución de prueba, el inicio y el final de cada prueba y el final de la ejecución de la prueba.

Así es como podría verse la implementación de HelloWorldTest con un único resultado de prueba fallido.

@Override
public void run(TestInformation testInfo, ITestInvocationListener listener) throws DeviceNotAvailableException {
    CLog.i("Hello, TF World! I have device " + testInfo.getDevice().getSerialNumber());

    TestDescription testId = new TestDescription("com.example.TestClassName", "sampleTest");
    listener.testRunStarted("helloworldrun", 1);
    listener.testStarted(testId);
    listener.testFailed(testId, "oh noes, test failed");
    listener.testEnded(testId, Collections.emptyMap());
    listener.testRunEnded(0, Collections.emptyMap());
}

TF incluye varias implementaciones de IRemoteTest que puede reutilizar en lugar de escribir las suyas propias desde cero. Por ejemplo, InstrumentationTest puede ejecutar las pruebas de una aplicación de Android de forma remota en un dispositivo Android, analizar los resultados y reenviar esos resultados a ITestInvocationListener ). Para obtener más información, consulte Tipos de prueba .

Almacenamiento de los resultados de la prueba (I)

La implementación de escucha de prueba predeterminada para una configuración TF es TextResultReporter , que descarga los resultados de una invocación a stdout. Para ilustrarlo, ejecute la configuración HelloWorldTest de la sección anterior:

./tradefed.sh
tf> run example/helloworld
04-29 18:25:55 I/TestInvocation: Invocation was started with cmd: /tmp/helloworld.xml
04-29 18:25:55 I/TestInvocation: Starting invocation for 'stub' with '[ BuildInfo{bid=0, target=stub, serial=876X00GNG} on device '876X00GNG']
04-29 18:25:55 I/HelloWorldTest: Hello, TF World! I have device 876X00GNG
04-29 18:25:55 I/InvocationToJUnitResultForwarder: Running helloworldrun: 1 tests
04-29 18:25:55 W/InvocationToJUnitResultForwarder:
Test com.example.TestClassName#sampleTest failed with stack:
 oh noes, test failed
04-29 18:25:55 I/InvocationToJUnitResultForwarder: Run ended in 0 ms

Para almacenar los resultados de una invocación en otro lugar, como en un archivo, especifique una implementación ITestInvocationListener personalizada utilizando la etiqueta result_reporter en su configuración.

TF también incluye el oyente XmlResultReporter , que escribe los resultados de la prueba en un archivo XML en un formato similar al utilizado por el escritor XML ant JUnit. Para especificar el result_reporter en la configuración, edite la configuración …/res/config/example/helloworld.xml :

<configuration description="Runs the hello world test">
    <test class="com.android.tradefed.example.HelloWorldTest" />
    <result_reporter class="com.android.tradefed.result.XmlResultReporter" />
</configuration>

Ahora reconstruya tradefed y vuelva a ejecutar la muestra de hola mundo:

tf> run example/helloworld
05-16 21:07:07 I/TestInvocation: Starting invocation for target stub on build 0 on device 004ad9880810a548
Hello, TF World! I have device 004ad9880810a548
05-16 21:07:07 I/XmlResultReporter: Saved device_logcat log to /tmp/0/inv_2991649128735283633/device_logcat_6999997036887173857.txt
05-16 21:07:07 I/XmlResultReporter: Saved host_log log to /tmp/0/inv_2991649128735283633/host_log_6307746032218561704.txt
05-16 21:07:07 I/XmlResultReporter: XML test result file generated at /tmp/0/inv_2991649128735283633/test_result_536358148261684076.xml. Total tests 1, Failed 1, Error 0

Observe el mensaje de registro que indica que se ha generado un archivo XML; el archivo generado debería verse así:

<?xml version='1.0' encoding='UTF-8' ?>
<testsuite name="stub" tests="1" failures="1" errors="0" time="9" timestamp="2011-05-17T04:07:07" hostname="localhost">
  <properties />
  <testcase name="sampleTest" classname="com.example.TestClassName" time="0">
    <failure>oh noes, test failed
    </failure>
  </testcase>
</testsuite>

También puede escribir sus propios escuchas de invocación personalizados; simplemente necesitan implementar la interfaz ITestInvocationListener .

Tradefed admite múltiples oyentes de invocación, por lo que puede enviar los resultados de las pruebas a múltiples destinos independientes. Para hacer esto, simplemente especifique varias etiquetas <result_reporter> en su configuración.

Registro (D, I, R)

Las instalaciones de tala de TF incluyen la capacidad de:

  1. Capturar registros del dispositivo (también conocido como dispositivo logcat)
  2. Registre los registros del marco de la Federación de Comercio que se ejecuta en la máquina host (también conocido como registro de host)

El marco TF captura automáticamente el logcat del dispositivo asignado y lo envía al oyente de invocación para su procesamiento. XmlResultReporter luego guarda el logcat del dispositivo capturado como un archivo.

Los registros de host de TF se informan mediante el contenedor CLog para la clase de registro ddmlib. CLog llamada System.out.println anterior en HelloWorldTest en una llamada CLog :

@Override
public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
    CLog.i("Hello, TF World! I have device %s", getDevice().getSerialNumber());

CLog maneja la interpolación de cadenas directamente, similar a String.format . Cuando reconstruya y vuelva a ejecutar TF, debería ver el mensaje de registro en stdout:

tf> run example/helloworld
…
05-16 21:30:46 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548
…

De forma predeterminada, tradefed envía mensajes de registro del host a stdout . TF también incluye una implementación de registro que escribe mensajes en un archivo: FileLogger . Para agregar el registro de archivos, agregue una etiqueta de logger a la configuración, especificando el nombre de clase completo de FileLogger :

<configuration description="Runs the hello world test">
    <test class="com.android.tradefed.example.HelloWorldTest" />
    <result_reporter class="com.android.tradefed.result.XmlResultReporter" />
    <logger class="com.android.tradefed.log.FileLogger" />
</configuration>

Ahora, reconstruya y ejecute el ejemplo de helloworld nuevamente:

tf >run example/helloworld
…
05-16 21:38:21 I/XmlResultReporter: Saved device_logcat log to /tmp/0/inv_6390011618174565918/device_logcat_1302097394309452308.txt
05-16 21:38:21 I/XmlResultReporter: Saved host_log log to /tmp/0/inv_6390011618174565918/host_log_4255420317120216614.txt
…

El mensaje de registro indica la ruta del registro del host, que, cuando se visualiza, debe contener su mensaje de registro HelloWorldTest:

more /tmp/0/inv_6390011618174565918/host_log_4255420317120216614.txt

Salida de ejemplo:

…
05-16 21:38:21 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548

Opciones de manipulación (D, I, R)

Los objetos cargados desde una configuración TF (también conocidos como objetos de configuración ) también pueden recibir datos de los argumentos de la línea de comando mediante el uso de la anotación @Option .

Para participar, una clase de objeto de configuración aplica la anotación @Option a un campo miembro y le proporciona un nombre único. Esto permite que el valor del campo de miembro se complete a través de una opción de línea de comando (y también agrega automáticamente esa opción al sistema de ayuda de configuración).

Nota: no se admiten todos los tipos de campo. Para obtener una descripción de los tipos admitidos, consulte OptionSetter .

@Option una @Option a HelloWorldTest:

@Option(name="my_option",
        shortName='m',
        description="this is the option's help text",
        // always display this option in the default help text
        importance=Importance.ALWAYS)
private String mMyOption = "thisisthedefault";

A continuación, agreguemos un mensaje de registro para mostrar el valor de la opción en HelloWorldTest para que podamos demostrar que se recibió correctamente:

@Override
public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
    …
    CLog.logAndDisplay(LogLevel.INFO, "I received option '%s'", mMyOption);

Finalmente, reconstruya TF y ejecute helloworld; debería ver un mensaje de registro con el valor predeterminado my_option :

tf> run example/helloworld
…
05-24 18:30:05 I/HelloWorldTest: I received option 'thisisthedefault'

Pasando valores desde la línea de comando

Pase un valor para my_option ; debería ver my_option con ese valor:

tf> run example/helloworld --my_option foo
…
05-24 18:33:44 I/HelloWorldTest: I received option 'foo'

Las configuraciones de TF también incluyen un sistema de ayuda, que muestra automáticamente el texto de ayuda para los campos @Option . Pruébelo ahora, y debería ver el texto de ayuda para my_option :

tf> run example/helloworld --help
Printing help for only the important options. To see help for all options, use the --help-all flag

  cmd_options options:
    --[no-]help          display the help text for the most important/critical options. Default: false.
    --[no-]help-all      display the full help text for all options. Default: false.
    --[no-]loop          keep running continuously. Default: false.

  test options:
    -m, --my_option      this is the option's help text Default: thisisthedefault.

  'file' logger options:
    --log-level-display  the minimum log level to display on stdout. Must be one of verbose, debug, info, warn, error, assert. Default: error.

Tenga en cuenta el mensaje sobre "imprimir solo las opciones importantes". Para reducir el desorden de la ayuda de opciones, TF usa el atributo Option#importance para determinar si mostrar un texto de ayuda de campo @Option particular cuando se especifica --help . --help-all siempre muestra ayuda para todos los campos de @Option , independientemente de su importancia. Para obtener más información, consulte Option.Importance .

Pasar valores de una configuración

También puede especificar un valor de Opción dentro de la configuración agregando un elemento <option name="" value=""> . helloworld.xml usando helloworld.xml :

<test class="com.android.tradefed.example.HelloWorldTest" >
    <option name="my_option" value="fromxml" />
</test>

La reconstrucción y ejecución de helloworld ahora debería producir este resultado:

05-24 20:38:25 I/HelloWorldTest: I received option 'fromxml'

La ayuda de configuración también debe actualizarse para indicar el valor predeterminado de my_option :

tf> run example/helloworld --help
  test options:
    -m, --my_option      this is the option's help text Default: fromxml.

Otros objetos de configuración incluidos en la configuración de helloworld, como FileLogger , también aceptan opciones. La opción --log-level-display es interesante porque filtra los registros que aparecen en stdout. Anteriormente en el tutorial, es posible que haya notado que el mensaje de registro "¡Hola, TF World! Tengo un dispositivo ..." dejó de mostrarse en la salida FileLogger después de que cambiamos al uso de FileLogger . Puede aumentar la verbosidad del registro en la FileLogger pasando el --log-level-display arg.

Pruebe esto ahora, y debería ver que el mensaje de registro 'Tengo un dispositivo' reaparecerá en la salida estándar, además de estar registrado en un archivo:

tf> run example/helloworld --log-level-display info
…
05-24 18:53:50 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548

¡Eso es todo amigos!

Como recordatorio, si está atascado en algo, el código fuente de la Federación de Comercio tiene mucha información útil que no se expone en la documentación. Si todo lo demás falla, intente preguntar en el grupo de Google de la plataforma Android , con "Federación de comercio" en el asunto del mensaje.