Con la introducción de las marcas de lanzamiento de funciones, existen nuevas políticas de pruebas que debes cumplir:
- Tus pruebas deben abarcar los comportamientos inhabilitados y habilitados de la marca.
- Debes usar los mecanismos oficiales para establecer valores de marca durante las pruebas.
- Las pruebas de xTS no deberían anular los valores de las marcas en las pruebas.
En la siguiente sección, se proporcionan los mecanismos oficiales que debes usar para cumplir con estas políticas.
Prueba el código marcado
Situación de prueba | Mecanismo utilizado |
---|---|
Pruebas locales cuando los valores de las marcas cambian con frecuencia | Android Debug Bridge, como se explica en Cómo cambiar el valor de una marca durante el tiempo de ejecución |
Pruebas locales cuando los valores de las marcas no cambian con frecuencia | Marca un archivo de valores como se explica en Cómo establecer valores de marcas de lanzamiento de funciones |
Pruebas de extremo a extremo en las que cambian los valores de las marcas | FeatureFlagTargetPreparer , como se describe en Cómo crear pruebas de extremo a extremo |
Prueba de unidades en las que cambian los valores de las marcas | SetFlagsRule con @EnableFlags y @DisableFlags , como se explica en Cómo crear pruebas de unidades (Java y Kotlin) o Cómo crear pruebas de unidades (C y C++) |
Pruebas de unidades o de extremo a extremo en las que los valores de las marcas no pueden cambiar | CheckFlagsRule como se describe en Cómo crear pruebas de unidades o de extremo a extremo en las que los valores de las marcas no cambien |
Crea pruebas de extremo a extremo
AOSP proporciona una clase llamada FeatureFlagTargetPreparer
, que permite realizar pruebas de extremo a extremo en un dispositivo. Esta clase acepta anulaciones de valores de marcas como entrada, establece esas marcas en la configuración de los dispositivos antes de la ejecución de prueba y restablece marcas después de la ejecución.
Puedes aplicar la funcionalidad de la clase FeatureFlagTargetPreparer
en el módulo de prueba y en los niveles de configuración de prueba.
Aplica FeatureFlagTargetPreparer en la configuración de un módulo de prueba
Para aplicar FeatureFlagTargetPreparer
en la configuración de un módulo de prueba, incluye FeatureFlagTargetPreparer
y anulaciones de valores de marcas en el archivo de configuración del módulo de prueba AndroidTest.xml
:
<target_preparer class="com.android.tradefed.targetprep.FeatureFlagTargetPreparer">
<option name="flag-value"
value="permissions/com.android.permission.flags.device_aware_permission_grant=true"/>
<option name="flag-value"
value="virtual_devices/android.companion.virtual.flags.stream_permissions=true"/>
</target_preparer>
donde:
target.preparer class
siempre se establece encom.android.tradefed.targetprep.FeatureFlagTargetPreparer
.option
es la anulación de la marca conname
siempre establecido enflag-value
yvalue
establecido ennamespace/aconfigPackage.flagName=true|false
.
Cómo crear módulos de prueba parametrizados basados en estados de marca
Para crear módulos de prueba parametrizados basados en estados de marca, haz lo siguiente:
Incluye
FeatureFlagTargetPreparer
en el archivo de configuración del módulo de pruebaAndroidTest.xml
:<target_preparer class="com.android.tradefed.targetprep.FeatureFlagTargetPreparer" >
Especifica las opciones de valor de la marca en la sección
test_module_config
de un archivo de compilaciónAndroid.bp
:android_test { name: "MyTest" ... } test_module_config { name: "MyTestWithMyFlagEnabled", base: "MyTest", ... options: [ {name: "flag-value", value: "telephony/com.android.internal.telephony.flags.oem_enabled_satellite_flag=true"}, ], } test_module_config { name: "MyTestWithMyFlagDisabled", base: "MyTest", ... options: [ {name: "flag-value", value: "telephony/com.android.internal.telephony.flags.carrier_enabled_satellite_flag=true"}, ], }
El campo
options
contiene las anulaciones de marca conname
siempre establecido enflag-value
yvalue
establecido ennamespace/aconfigPackage.flagName=true|false
.
Cómo crear pruebas de unidades (Java y Kotlin)
En esta sección, se describe el enfoque para anular los valores de las marcas aconfig a nivel de la clase y del método (por prueba) en las pruebas de Java y Kotlin.
Para escribir pruebas de unidades automatizadas en una base de código grande con una gran cantidad de marcas, sigue estos pasos:
- Usa la clase
SetFlagsRule
con las anotaciones@EnableFlags
y@DisableFlags
para probar todas las ramas de código. - Usa el método
SetFlagsRule.ClassRule
para evitar errores de prueba comunes. - Usa
FlagsParameterization
para probar tus clases en un amplio conjunto de configuraciones de marcas.
Cómo probar todas las ramas de código
En el caso de los proyectos que usan la clase estática para acceder a las marcas, se proporciona la clase auxiliar SetFlagsRule
para anular los valores de las marcas. En el siguiente fragmento de código, se muestra cómo incluir SetFlagsRule
y habilitar varias marcas a la vez:
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import com.example.android.aconfig.demo.flags.Flags;
...
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Test
@EnableFlags({Flags.FLAG_FLAG_FOO, Flags.FLAG_FLAG_BAR})
public void test_flag_foo_and_flag_bar_turned_on() {
...
}
donde:
@Rule
es una anotación que se usa para agregar la dependencia de JUnit de la claseSetFlagsRule
.SetFlagsRule
es una clase auxiliar que se proporciona para anular los valores de las marcas. Para obtener información sobre cómoSetFlagsRule
determina los valores predeterminados, consulta Valores predeterminados del dispositivo.@EnableFlags
es una anotación que acepta una cantidad arbitraria de nombres de marcas. Cuando inhabilites marcas, usa@DisableFlags
. Puedes aplicar estas anotaciones a un método o una clase.
Establece valores de marcas para todo el proceso de prueba, comenzando con SetFlagsRule
, que es anterior a cualquier método de configuración anotado @Before
en la prueba. Los valores de las marcas vuelven a su estado anterior cuando finaliza SetFlagsRule
, que es después de cualquier método de configuración anotado en @After
.
Asegúrate de que las marcas estén configuradas correctamente
Como se mencionó anteriormente, SetFlagsRule
se usa con la anotación @Rule
de JUnit, lo que significa que SetFlagsRule
no puede garantizar que tus marcas se configuren correctamente durante el constructor de la clase de prueba ni ningún método con anotaciones @BeforeClass
o @AfterClass
.
Para asegurarte de que los dispositivos de prueba se construyan con el valor de clase correcto, usa el método SetFlagsRule.ClassRule
de modo que no se creen tus dispositivos hasta que se anote un método de configuración anotado @Before
:
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import com.example.android.aconfig.demo.flags.Flags;
class ExampleTest {
@ClassRule public static final SetFlagsRule.ClassRule mClassRule = new SetFlagsRule.ClassRule();
@Rule public final SetFlagsRule mSetFlagsRule = mClassRule.createSetFlagsRule();
private DemoClass underTest = new DemoClass();
@Test
@EnableFlags(Flags.FLAG_FLAG_FOO)
public void test_flag_foo_turned_on() {
...
}
}
Si agregas la regla de clase SetFlagsRule.ClassRule
, test_flag_foo_turned_on
falla antes de ejecutarse cuando el constructor de DemoClass
lee FLAG_FLAG_FOO
.
Si toda la clase necesita una marca habilitada, mueve la anotación @EnableFlags
al nivel de la clase (antes de la declaración de la clase). Mover la anotación al nivel de clase permite que SetFlagsRule.ClassRule
se asegure de que la marca esté configurada correctamente durante el constructor de la clase de prueba o durante cualquier método anotado @BeforeClass
o @AfterClass
.
Ejecuta pruebas en varias configuraciones de marcas
Debido a que puedes configurar los valores de las marcas por prueba, también puedes usar la parametrización para ejecutar pruebas en varias configuraciones de marcas:
...
import com.example.android.aconfig.demo.flags.Flags;
...
@RunWith(ParameterizedAndroidJunit4::class)
class FooBarTest {
@Parameters(name = "{0}")
public static List<FlagsParameterization> getParams() {
return FlagsParameterization.allCombinationsOf(Flags.FLAG_FOO, Flags.FLAG_BAR);
}
@Rule
public SetFlagsRule mSetFlagsRule;
public FooBarTest(FlagsParameterization flags) {
mSetFlagsRule = new SetFlagsRule(flags);
}
@Test public void fooLogic() {...}
@DisableFlags(Flags.FLAG_BAR)
@Test public void legacyBarLogic() {...}
@EnableFlags(Flags.FLAG_BAR)
@Test public void newBarLogic() {...}
}
Ten en cuenta que con SetFlagsRule
, pero sin la parametrización, esta clase ejecuta tres pruebas (fooLogic
, legacyBarLogic
y newBarLogic
). El método fooLogic
se ejecuta con los valores de FLAG_FOO
y FLAG_BAR
configurados en el dispositivo.
Cuando se agrega la parametrización, el método FlagsParameterization.allCombinationsOf
crea todas las combinaciones posibles de las marcas FLAG_FOO
y FLAG_BAR
:
FLAG_FOO
estrue
yFLAG_BAR
estrue
FLAG_FOO
estrue
yFLAG_BAR
esfalse
FLAG_FOO
esfalse
yFLAG_BAR
estrue
FLAG_FOO
es falso yFLAG_BAR
esfalse
En lugar de cambiar directamente los valores de las marcas, las anotaciones @DisableFlags
y @EnableFlags
modifican los valores de las marcas según las condiciones de los parámetros. Por ejemplo, legacyBarLogic
se ejecuta solo cuando FLAG_BAR
está inhabilitado, lo que ocurre en dos de las cuatro combinaciones de marcas. El legacyBarLogic
se omite para las otras dos combinaciones.
Existen dos métodos para crear las parametrizaciones de tus marcas:
FlagsParameterization.allCombinationsOf(String...)
ejecuta 2^n ejecuciones de cada prueba. Por ejemplo, una marca ejecuta 2 pruebas o cuatro marcas ejecutan 16 pruebas.FlagsParameterization.progressionOf(String...)
ejecuta n + 1 ejecuciones de cada prueba. Por ejemplo, una marca ejecuta 2 veces las pruebas y cuatro marcas ejecutan 5 veces las marcas.
Cómo crear pruebas de unidades (C y C++)
AOSP incluye macros de valores de marca para pruebas de C y C++ escritas en el framework de GoogleTest.
En la fuente de prueba, incluye las definiciones de macro y las bibliotecas generadas por aconfig:
#include <flag_macros.h> #include "android_cts_flags.h"
En la fuente de prueba, en lugar de usar las macros
TEST
yTESTF
para tus casos de prueba, usaTEST_WITH_FLAGS
yTEST_F_WITH_FLAGS
:#define TEST_NS android::cts::flags::tests ... TEST_F_WITH_FLAGS( TestFWithFlagsTest, requies_disabled_flag_enabled_skip, REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(TEST_NS, readwrite_enabled_flag)) ) { TestFail(); } ... TEST_F_WITH_FLAGS( TestFWithFlagsTest, multi_flags_for_same_state_skip, REQUIRES_FLAGS_ENABLED( ACONFIG_FLAG(TEST_NS, readwrite_enabled_flag), LEGACY_FLAG(aconfig_flags.cts, TEST_NS, readwrite_disabled_flag) ) ) { TestFail(); } ... TEST_WITH_FLAGS( TestWithFlagsTest, requies_disabled_flag_enabled_skip, REQUIRES_FLAGS_DISABLED( LEGACY_FLAG(aconfig_flags.cts, TEST_NS, readwrite_enabled_flag)) ) { FAIL(); } ... TEST_WITH_FLAGS( TestWithFlagsTest, requies_enabled_flag_enabled_executed, REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_NS, readwrite_enabled_flag)) ) { TestWithFlagsTestHelper::executed_tests.insert( "requies_enabled_flag_enabled_executed"); }
donde:
- Se usan macros
TEST_WITH_FLAGS
yTEST_F_WITH_FLAGS
en lugar de macrosTEST
yTEST_F
. REQUIRES_FLAGS_ENABLED
define un conjunto de marcas de lanzamiento de funciones que deben cumplir con la condición habilitada. Puedes escribir estas marcas en macrosACONFIG_FLAG
oLEGACY_FLAG
.REQUIRES_FLAGS_DISABLED
define un conjunto de marcas de función que debe cumplir con la condición inhabilitada. Puedes escribir estas marcas en macrosACONFIG_FLAG
oLEGACY_FLAG
.ACONFIG_FLAG (TEST_NS, readwrite_enabled_flag)
es una macro que se usa para las marcas definidas en los archivos aconfig. Esta macro acepta un espacio de nombres (TEST_NS
) y un nombre de marca (readwrite_enabled_flag
).LEGACY_FLAG(aconfig_flags.cts, TEST_NS, readwrite_disabled_flag)
es una macro que se usa para marcas establecidas en la configuración del dispositivo de forma predeterminada.
- Se usan macros
En el archivo de compilación
Android.bp
, agrega las bibliotecas generadas por aconfig y las bibliotecas de macros relevantes como dependencia de prueba:cc_test { name: "FlagMacrosTests", srcs: ["src/FlagMacrosTests.cpp"], static_libs: [ "libgtest", "libflagtest", "my_aconfig_lib", ], shared_libs: [ "libbase", "server_configurable_flags", ], test_suites: ["general-tests"], ... }
Ejecuta las pruebas de forma local con este comando:
atest FlagMacrosTests
Si la marca
my_namespace.android.myflag.tests.my_flag
está inhabilitada, el resultado de la prueba es el siguiente:[1/2] MyTest#test1: IGNORED (0ms) [2/2] MyTestF#test2: PASSED (0ms)
Si la marca
my_namespace.android.myflag.tests.my_flag
está habilitada, el resultado de la prueba es el siguiente:[1/2] MyTest#test1: PASSED (0ms) [2/2] MyTestF#test2: IGNORED (0ms)
Crea pruebas de unidades o de extremo a extremo en las que los valores de las marcas no cambien.
Para los casos de prueba en los que no puedes anular marcas y filtrar pruebas solo si
se basan en el estado de la marca actual, usa la regla CheckFlagsRule
con
anotaciones RequiresFlagsEnabled
y RequiresFlagsDisabled
.
En los siguientes pasos, se muestra cómo crear y ejecutar una prueba de unidades o de extremo a extremo en la que los valores de las marcas no se pueden anular:
En tu código de prueba, usa
CheckFlagsRule
para aplicar el filtrado de prueba. Además, usa las anotaciones de JavaRequiresFlagsEnabled
yRequiredFlagsDisabled
para especificar los requisitos de marca de tu prueba.La prueba del dispositivo usa la clase
DeviceFlagsValueProvider
:@RunWith(JUnit4.class) public final class FlagAnnotationTest { @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @Test @RequiresFlagsEnabled(Flags.FLAG_FLAG_NAME_1) public void test1() {} @Test @RequiresFlagsDisabled(Flags.FLAG_FLAG_NAME_1) public void test2() {} }
La prueba del host usa la clase
HostFlagsValueProvider
:@RunWith(DeviceJUnit4ClassRunner.class) public final class FlagAnnotationTest extends BaseHostJUnit4Test { @Rule public final CheckFlagsRule mCheckFlagsRule = HostFlagsValueProvider.createCheckFlagsRule(this::getDevice); @Test @RequiresFlagsEnabled(Flags.FLAG_FLAG_NAME_1) public void test1() {} @Test @RequiresFlagsDisabled(Flags.FLAG_FLAG_NAME_1) public void test2() {} }
Agrega
jflag-unit
y las bibliotecas generadas por aconfig a la secciónstatic_libs
del archivo de compilación para tu prueba:android_test { name: "FlagAnnotationTests", srcs: ["*.java"], static_libs: [ "androidx.test.rules", "my_aconfig_lib", "flag-junit", "platform-test-annotations", ], test_suites: ["general-tests"], }
Usa el siguiente comando para ejecutar la prueba de forma local:
atest FlagAnnotationTests
Si la marca
Flags.FLAG_FLAG_NAME_1
está inhabilitada, el resultado de la prueba es el siguiente:[1/2] com.cts.flags.FlagAnnotationTest#test1: ASSUMPTION_FAILED (10ms) [2/2] com.cts.flags.FlagAnnotationTest#test2: PASSED (2ms)
De lo contrario, el resultado de la prueba es el siguiente:
[1/2] com.cts.flags.FlagAnnotationTest#test1: PASSED (2ms) [2/2] com.cts.flags.FlagAnnotationTest#test2: ASSUMPTION_FAILED (10ms)
Valores predeterminados del dispositivo
La SetFlagsRule
inicializada usa valores de marcas del dispositivo. Si el valor de la marca en el dispositivo no se anula, como con adb, el valor predeterminado es el mismo que la configuración de lanzamiento de la compilación. Si se anuló el valor del dispositivo, SetFlagsRule
usa el valor de anulación como el predeterminado.
Si se ejecuta la misma prueba con diferentes configuraciones de lanzamiento, el valor de las marcas que no se establecen de forma explícita con SetFlagsRule
puede variar.
Después de cada prueba, SetFlagsRule
restablece la instancia FeatureFlags
en Flags
a su FeatureFlagsImpl
original, de modo que no tenga efectos secundarios en otros métodos y clases de prueba.