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 class
siempre se configura encom.android.tradefed.targetprep.FeatureFlagTargetPreparer
.option
es la anulación de la marca conname
siempre configurado enflag-value
yvalue
configurado 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
FeatureFlagTargetPreparer
en 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_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 anulaciones de marcas conname
siempre configurado enflag-value
yvalue
configurado 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
SetFlagsRule
con las anotaciones@EnableFlags
y@DisableFlags
para probar todas las ramas de código. - Usa el método
SetFlagsRule.ClassRule
para evitar errores comunes en las pruebas. - 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 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:
@Rule
es una anotación que se utiliza para agregar la dependencia flag-JUnit de la claseSetFlagsRule
.SetFlagsRule
es la clase de ayuda que se proporciona para anular valores de marcas. Si quieres obtener más información sobre el modo queSetFlagsRule
determina valores predeterminados, consulta Valores predeterminados del dispositivo.@EnableFlags
es 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_FOO
estrue
yFLAG_BAR
estrue
.FLAG_FOO
estrue
yFLAG_BAR
esfalse
.FLAG_FOO
esfalse
yFLAG_BAR
estrue
.FLAG_FOO
es false yFLAG_BAR
esfalse
.
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
TEST
yTESTF
para los 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"); }
En la que:
- Se usan las macros
TEST_WITH_FLAGS
yTEST_F_WITH_FLAGS
en lugar de las macrosTEST
yTEST_F
macros. REQUIRES_FLAGS_ENABLED
define un conjunto de marcas de lanzamiento de funciones que deben cumplir con la condición de habilitadas. Puedes escribir estas marcas en las macrosACONFIG_FLAG
oLEGACY_FLAG
.REQUIRES_FLAGS_DISABLED
define un conjunto de marcas de función que deben cumplir con la condición de inhabilitadas. Puedes escribir estas marcas en las macrosACONFIG_FLAG
oLEGACY_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 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)
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
CheckFlagsRule
para aplicar filtros de prueba. Además, usa las anotaciones JavaRequiresFlagsEnabled
yRequiredFlagsDisabled
para 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-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 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.