Con la incorporación de las marcas de lanzamiento de funciones, hay nuevas políticas de pruebas con las que debes cumplir:
- Tus pruebas deben abarcar los comportamientos inhabilitados y habilitados de la marca.
- Debes usar los mecanismos oficiales para establecer valores de las marcas durante las pruebas.
- Las pruebas de xTS no deben 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.
Cómo probar el código con marcas
| Escenario de prueba | Mecanismo utilizado |
|---|---|
| Pruebas locales cuando los valores de las marcas cambian a menudo | Android Debug Bridge, como se menciona 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 a menudo | Valores de marcas, como se menciona 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 menciona en Cómo crear pruebas de extremo a extremo |
| Pruebas de unidades en las que cambian los valores de las marcas | SetFlagsRule con @EnableFlags y @DisableFlags, como se menciona 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 no pueden cambiar los valores de las marcas | CheckFlagsRule, como se menciona en Cómo crear pruebas de unidades o de extremo a extremo en las que no cambian los valores de las marcas |
Cómo crear pruebas de extremo a extremo
AOSP proporciona una clase llamada FeatureFlagTargetPreparer, que habilita las pruebas de extremo a extremo en un dispositivo. Esta clase acepta anulaciones de los valores de las marcas como entrada, establece esas marcas en la configuración del dispositivo antes de la ejecución de prueba y restablece las marcas después de la ejecución.
Puedes aplicar la funcionalidad de la clase FeatureFlagTargetPreparer en el módulo de prueba y probar los niveles de configuración.
Cómo aplicar 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 los valores de las 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>
En la que:
target.preparer classsiempre se configura encom.android.tradefed.targetprep.FeatureFlagTargetPreparer.optiones la anulación de la marca connamesiempre configurado enflag-valueyvalueconfigurado ennamespace/aconfigPackage.flagName=true|false.
Cómo crear módulos de prueba parametrizados basados en estados de marcas
Para crear módulos de prueba parametrizados basados en estados de marcas, haz lo siguiente:
Incluye
FeatureFlagTargetPrepareren el archivo de configuración del módulo de pruebaAndroidTest.xml:<target_preparer class="com.android.tradefed.targetprep.FeatureFlagTargetPreparer" >Especifica opciones de valores de marcas en la sección
test_module_configde 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
optionscontiene anulaciones de marcas connamesiempre configurado enflag-valueyvalueconfigurado ennamespace/aconfigPackage.flagName=true|false.
Cómo crear pruebas de unidades (Java y Kotlin)
En esta sección, se describe la forma de anular valores de marcas de aconfig en el nivel de clase y método (por prueba) en 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
SetFlagsRulecon las anotaciones@EnableFlagsy@DisableFlagspara probar todas las ramas de código. - Usa el método
SetFlagsRule.ClassRulepara evitar errores comunes en las pruebas. - Usa
FlagsParameterizationpara 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 de ayuda SetFlagsRule para anular 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() {
...
}
En la que:
@Rulees una anotación que se utiliza para agregar la dependencia flag-JUnit de la claseSetFlagsRule.SetFlagsRulees la clase de ayuda que se proporciona para anular valores de marcas. Si quieres obtener más información sobre el modo queSetFlagsRuledetermina valores predeterminados, consulta Valores predeterminados del dispositivo.@EnableFlagses una anotación que acepta un número arbitrario 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 pruebas, comenzando con SetFlagsRule, que es anterior a cualquier método de configuración anotado con @Before en la prueba. Los valores de las marcas regresan al estado anterior cuando termina SetFlagsRule, que es posterior a cualquier método de configuración anotado con @After.
Cómo garantizar que las marcas estén configuradas correctamente
Como ya se mencionó, SetFlagsRule se usa con la anotación @Rule de JUnit, lo que significa que SetFlagsRule no puede garantizar que tus marcas estén configuradas correctamente durante el constructor de la clase de prueba o cualquier método anotado con @BeforeClass o @AfterClass.
Para asegurarte de que los accesorios de prueba se construyan con el valor de clase correcto, usa el método SetFlagsRule.ClassRule para que estos no se creen hasta que haya un método de configuración anotado con @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 fallará antes de ejecutarse cuando el constructor de DemoClass lea FLAG_FLAG_FOO.
Si toda la clase necesita tener una marca habilitada, mueve la anotación @EnableFlags al nivel de clase (antes de la declaración de la clase). Mover la anotación al nivel de clase permite que SetFlagsRule.ClassRule garantice que la marca está configurada correctamente durante el constructor de la clase de prueba o cualquier método anotado con @AfterClass o @BeforeClass.
Cómo ejecutar pruebas en múltiples configuraciones de marcas
Dado que puedes establecer valores de marcas prueba por prueba, también puedes usar la parametrización para ejecutar pruebas en múltiples 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 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 que estén 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_FOOestrueyFLAG_BARestrue.FLAG_FOOestrueyFLAG_BAResfalse.FLAG_FOOesfalseyFLAG_BARestrue.FLAG_FOOes false yFLAG_BAResfalse.
En lugar de cambiar directamente los valores de las marcas, las anotaciones @DisableFlags y @EnableFlags modifican los valores en función de las condiciones del parámetro. Por ejemplo, legacyBarLogic se ejecuta solo cuando se inhabilita FLAG_BAR, lo que ocurre en dos de las cuatro combinaciones de marcas. legacyBarLogic se omite para las otras dos combinaciones.
Existen dos métodos para crear la parametrización de tus marcas:
FlagsParameterization.allCombinationsOf(String...)ejecuta 2^n instancias de cada prueba. Por ejemplo, una marca ejecuta pruebas 2 veces o cuatro marcas ejecutan pruebas 16 veces.FlagsParameterization.progressionOf(String...)ejecuta n+1 instancias de cada prueba. Por ejemplo, una marca ejecuta pruebas 2 veces y cuatro marcas ejecutan pruebas 5 veces.
Cómo crear pruebas de unidades (C y C++)
AOSP incluye macros de valores de marcas para pruebas en C y C++ escritas en el framework de GoogleTest.
En la fuente de prueba, incluye las definiciones de la 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
TESTyTESTFpara los casos de prueba, usaTEST_WITH_FLAGSyTEST_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"); }En la que:
- Se usan las macros
TEST_WITH_FLAGSyTEST_F_WITH_FLAGSen lugar de las macrosTESTyTEST_Fmacros. REQUIRES_FLAGS_ENABLEDdefine un conjunto de marcas de lanzamiento de funciones que deben cumplir con la condición de habilitadas. Puedes escribir estas marcas en las macrosACONFIG_FLAGoLEGACY_FLAG.REQUIRES_FLAGS_DISABLEDdefine un conjunto de marcas de función que deben cumplir con la condición de inhabilitadas. Puedes escribir estas marcas en las macrosACONFIG_FLAGoLEGACY_FLAG.ACONFIG_FLAG (TEST_NS, readwrite_enabled_flag)es una macro que se utiliza para las marcas definidas en archivos de 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 utiliza para las marcas definidas en la configuración del dispositivo de forma predeterminada.
- Se usan las macros
En tu archivo de compilación
Android.bp, agrega las bibliotecas generadas por aconfig y las bibliotecas de macros pertinentes como una 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 manera local con este comando:
atest FlagMacrosTestsSi la marca
my_namespace.android.myflag.tests.my_flagestá 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_flagestá habilitada, el resultado de la prueba es el siguiente:[1/2] MyTest#test1: PASSED (0ms) [2/2] MyTestF#test2: IGNORED (0ms)
Cómo crear pruebas de unidades o de extremo a extremo en las que no cambian los valores de las marcas
En los casos de prueba en los que no puedes anular marcas y puedes filtrar pruebas solo si se basan en el estado actual de la marca, usa la regla CheckFlagsRule con las anotaciones RequiresFlagsEnabled y RequiresFlagsDisabled.
Los pasos siguientes te muestran cómo crear y ejecutar pruebas de unidades o de extremo a extremo en las que los valores de las marcas no se pueden anular:
En el código de prueba, usa
CheckFlagsRulepara aplicar filtros de prueba. Además, usa las anotaciones JavaRequiresFlagsEnabledyRequiredFlagsDisabledpara especificar los requisitos de las marcas para tu prueba.La prueba del lado 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 lado 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-unity las bibliotecas generadas por aconfig a la secciónstatic_libsdel 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 FlagAnnotationTestsSi la marca
Flags.FLAG_FLAG_NAME_1está inhabilitada, el resultado de la prueba será 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 será:
[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 el de la configuración de versión de la compilación. Si se anuló el valor en el dispositivo, SetFlagsRule usa el valor de anulación como predeterminado.
Si se ejecuta la misma prueba bajo configuraciones de versión diferentes, el valor de las marcas que no se estableció de manera explícita con SetFlagsRule puede variar.
Después de cada prueba, SetFlagsRule restablece la instancia de FeatureFlags en Flags a su FeatureFlagsImpl original para que no genere efectos secundarios en otros métodos y clases de prueba.