Avec l'introduction des indicateurs de lancement de fonctionnalité, vous devez respecter de nouvelles règles de test:
- Vos tests doivent couvrir les comportements activés et désactivés de l'indicateur.
- Vous devez utiliser les mécanismes officiels pour définir les valeurs des indicateurs lors des tests.
- Les tests xTS ne doivent pas remplacer les valeurs d'indicateur dans les tests.
La section suivante présente les mécanismes officiels à utiliser pour adhérer à ces règles.
Tester le code signalé
Scénario de test | Mécanisme utilisé |
---|---|
Tests locaux lorsque les valeurs des indicateurs changent fréquemment | Android Debug Bridge, comme indiqué dans la section Modifier la valeur d'un indicateur au moment de l'exécution |
Tests en local lorsque les valeurs des indicateurs ne changent pas souvent | Fichier de valeurs d'indicateur, comme indiqué dans Définir les valeurs des indicateurs de lancement de fonctionnalités |
Tests de bout en bout dans lesquels les valeurs d'indicateur changent | FeatureFlagTargetPreparer , comme indiqué dans la section Créer des tests de bout en bout |
Tests unitaires dans lesquels les valeurs des options changent | SetFlagsRule avec @EnableFlags et @DisableFlags , comme indiqué dans les sections Créer des tests unitaires (Java et Kotlin) ou Créer des tests unitaires (C et C++). |
Tests unitaires ou de bout en bout pour lesquels les valeurs des options ne peuvent pas être modifiées | CheckFlagsRule , comme indiqué dans Créer des tests unitaires ou de bout en bout où les valeurs d'indicateur ne changent pas |
Créer des tests de bout en bout
AOSP fournit une classe appelée FeatureFlagTargetPreparer
, qui permet d'effectuer des tests de bout en bout sur un appareil. Cette classe accepte les remplacements de valeur d'indicateur en tant qu'entrée, définit ces indicateurs dans la configuration des appareils avant l'exécution du test et restaure les indicateurs après l'exécution.
Vous pouvez appliquer la fonctionnalité de la classe FeatureFlagTargetPreparer
au niveau du module et de la configuration de test.
Appliquer FeatureFlagTargetPreparer dans une configuration de module de test
Pour appliquer FeatureFlagTargetPreparer
dans une configuration de module de test, incluez des forçages de valeur FeatureFlagTargetPreparer
et d'indicateur dans le fichier de configuration du module de test 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>
Où :
target.preparer class
est toujours défini surcom.android.tradefed.targetprep.FeatureFlagTargetPreparer
.option
est le forçage d'option, avecname
toujours défini surflag-value
etvalue
surnamespace/aconfigPackage.flagName=true|false
.
Créer des modules de test paramétrés en fonction des états des indicateurs
Pour créer des modules de test paramétrés en fonction de l'état des indicateurs:
Incluez
FeatureFlagTargetPreparer
dans le fichier de configuration du module de testAndroidTest.xml
:<target_preparer class="com.android.tradefed.targetprep.FeatureFlagTargetPreparer" >
Spécifiez les options de valeur d'indicateur dans la section
test_module_config
d'un fichier de compilationAndroid.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"}, ], }
Le champ
options
contient les remplacements d'indicateur avecname
toujours défini surflag-value
etvalue
défini surnamespace/aconfigPackage.flagName=true|false
.
Créer des tests unitaires (Java et Kotlin)
Cette section décrit l'approche permettant de remplacer les valeurs d'indicateur aconfig au niveau de la classe et de la méthode (par test) dans les tests Java et Kotlin.
Pour écrire des tests unitaires automatisés dans un grand codebase avec un grand nombre d'indicateurs, procédez comme suit :
- Utilisez la classe
SetFlagsRule
avec les annotations@EnableFlags
et@DisableFlags
pour tester toutes les branches de code. - Utilisez la méthode
SetFlagsRule.ClassRule
pour éviter les bugs de test courants. - Utilisez
FlagsParameterization
pour tester vos classes sur un large ensemble de configurations d'indicateurs.
Tester toutes les branches de code
Pour les projets qui utilisent la classe statique pour accéder aux options, la classe d'assistance SetFlagsRule
est fournie pour remplacer les valeurs des options. L'extrait de code suivant montre comment inclure SetFlagsRule
et activer plusieurs indicateurs à la fois :
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() {
...
}
Où :
@Rule
est une annotation utilisée pour ajouter la dépendance flag-JUnit de la classeSetFlagsRule
.SetFlagsRule
est une classe d'assistance fournie pour remplacer les valeurs d'indicateur. Pour en savoir plus sur la façon dontSetFlagsRule
détermine les valeurs par défaut, consultez la section Valeurs par défaut de l'appareil.@EnableFlags
est une annotation qui accepte un nombre arbitraire de noms d'indicateurs. Lorsque vous désactivez les options, utilisez@DisableFlags
. Vous pouvez appliquer ces annotations à une méthode ou à une classe.
Définissez des valeurs d'indicateur pour l'ensemble du processus de test, en commençant par SetFlagsRule
, qui précède toute méthode de configuration annotée par @Before
dans le test. Les valeurs des options reviennent à leur état précédent lorsque l'SetFlagsRule
se termine, c'est-à-dire après toutes les méthodes de configuration annotées par @After
.
Vérifier que les indicateurs sont correctement définis
Comme indiqué précédemment, SetFlagsRule
est utilisé avec l'annotation @Rule
de JUnit, ce qui signifie que SetFlagsRule
ne peut pas garantir que vos options sont correctement définies lors du constructeur de la classe de test ou des méthodes annotées avec @BeforeClass
ou @AfterClass
.
Pour vous assurer que les fixtures de test sont créées avec la valeur de classe appropriée, utilisez la méthode SetFlagsRule.ClassRule
afin que vos fixtures ne soient pas créées avant une méthode de configuration annotée @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() {
...
}
}
En ajoutant la règle de classe SetFlagsRule.ClassRule
, test_flag_foo_turned_on
échoue avant l'exécution lorsque FLAG_FLAG_FOO
est lu par le constructeur de DemoClass
.
Si un indicateur doit être activé pour l'ensemble de votre classe, déplacez l'annotation @EnableFlags
au niveau de la classe (avant la déclaration de classe). Déplacer l'annotation au niveau de la classe permet à SetFlagsRule.ClassRule
de s'assurer que l'indicateur est défini correctement lors du constructeur de la classe de test ou lors de toute méthode annotée @BeforeClass
ou @AfterClass
.
Exécuter des tests sur plusieurs configurations d'indicateurs
Étant donné que vous pouvez définir des valeurs d'indicateur par test, vous pouvez également utiliser le paramétrage pour exécuter des tests sur plusieurs configurations d'indicateurs:
...
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() {...}
}
Notez qu'avec SetFlagsRule
, mais sans paramétrage, cette classe exécute trois tests (fooLogic
, legacyBarLogic
et newBarLogic
). La méthode fooLogic
s'exécute avec les valeurs de FLAG_FOO
et FLAG_BAR
définies sur l'appareil.
Lorsque le paramétrage est ajouté, la méthode FlagsParameterization.allCombinationsOf
crée toutes les combinaisons possibles des indicateurs FLAG_FOO
et FLAG_BAR
:
FLAG_FOO
esttrue
etFLAG_BAR
esttrue
FLAG_FOO
esttrue
etFLAG_BAR
estfalse
FLAG_FOO
estfalse
etFLAG_BAR
esttrue
FLAG_FOO
est "false" etFLAG_BAR
estfalse
Au lieu de modifier directement les valeurs des options, les annotations @DisableFlags
et @EnableFlags
modifient les valeurs des options en fonction des conditions des paramètres. Par exemple, legacyBarLogic
ne s'exécute que lorsque FLAG_BAR
est désactivé, ce qui se produit dans deux des quatre combinaisons d'indicateurs. legacyBarLogic
est ignoré pour les deux autres combinaisons.
Il existe deux méthodes pour créer les paramétrisations de vos indicateurs :
FlagsParameterization.allCombinationsOf(String...)
exécute 2^n exécutions de chaque test. Par exemple, un indicateur exécute 2 tests ou 4 fois 16 fois les tests.FlagsParameterization.progressionOf(String...)
exécute n + 1 exécutions de chaque test. Par exemple, un indicateur exécute deux tests, et quatre indicateurs 5 fois.
Créer des tests unitaires (C et C++)
AOSP inclut des macros de valeur d'indicateur pour les tests C et C++ écrits dans le framework GoogleTest.
Dans votre source de test, incluez les définitions de macro et les bibliothèques générées par aconfig:
#include <flag_macros.h> #include "android_cts_flags.h"
Dans votre source de test, au lieu d'utiliser des macros
TEST
etTESTF
pour vos cas de test, utilisezTEST_WITH_FLAGS
etTEST_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"); }
Où :
- Les macros
TEST_WITH_FLAGS
etTEST_F_WITH_FLAGS
sont utilisées à la place des macrosTEST
etTEST_F
. REQUIRES_FLAGS_ENABLED
définit un ensemble d'indicateurs de lancement de fonctionnalités qui doivent remplir la condition activée. Vous pouvez écrire ces indicateurs dans les macrosACONFIG_FLAG
ouLEGACY_FLAG
.REQUIRES_FLAGS_DISABLED
définit un ensemble d'indicateurs de fonctionnalité qui doivent remplir la condition "désactivé". Vous pouvez écrire ces indicateurs dans les macrosACONFIG_FLAG
ouLEGACY_FLAG
.ACONFIG_FLAG (TEST_NS, readwrite_enabled_flag)
est une macro utilisée pour les indicateurs définis dans des fichiers de configuration. Cette macro accepte un espace de noms (TEST_NS
) et un nom d'indicateur (readwrite_enabled_flag
).LEGACY_FLAG(aconfig_flags.cts, TEST_NS, readwrite_disabled_flag)
est une macro utilisée par défaut pour les indicateurs définis dans la configuration de l'appareil.
- Les macros
Dans votre fichier de compilation
Android.bp
, ajoutez les bibliothèques générées par aconfig et les bibliothèques de macros pertinentes en tant que dépendance de test: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"], ... }
Exécutez les tests en local à l'aide de la commande suivante :
atest FlagMacrosTests
Si l'indicateur
my_namespace.android.myflag.tests.my_flag
est désactivé, le résultat du test est le suivant :[1/2] MyTest#test1: IGNORED (0ms) [2/2] MyTestF#test2: PASSED (0ms)
Si l'indicateur
my_namespace.android.myflag.tests.my_flag
est activé, le résultat du test est le suivant :[1/2] MyTest#test1: PASSED (0ms) [2/2] MyTestF#test2: IGNORED (0ms)
Créer des tests unitaires ou de bout en bout où les valeurs des indicateurs ne changent pas
Pour les cas de test où vous ne pouvez pas remplacer les indicateurs et ne pouvez filtrer les tests que s'ils sont basés sur l'état actuel de l'indicateur, utilisez la règle CheckFlagsRule
avec les annotations RequiresFlagsEnabled
et RequiresFlagsDisabled
.
Les étapes suivantes vous expliquent comment créer et exécuter un test unitaire ou de bout en bout dans lequel les valeurs d'indicateur ne peuvent pas être remplacées:
Dans votre code de test, utilisez
CheckFlagsRule
pour appliquer le filtrage du test. Utilisez également les annotations JavaRequiresFlagsEnabled
etRequiredFlagsDisabled
pour spécifier les exigences de l'indicateur pour votre test.Le test côté appareil utilise la classe
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() {} }
Le test côté hôte utilise la classe
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() {} }
Ajoutez
jflag-unit
et les bibliothèques générées par aconfig à la sectionstatic_libs
du fichier de compilation pour votre test:android_test { name: "FlagAnnotationTests", srcs: ["*.java"], static_libs: [ "androidx.test.rules", "my_aconfig_lib", "flag-junit", "platform-test-annotations", ], test_suites: ["general-tests"], }
Exécutez le test en local à l'aide de la commande suivante:
atest FlagAnnotationTests
Si l'indicateur
Flags.FLAG_FLAG_NAME_1
est désactivé, le résultat du test est le suivant :[1/2] com.cts.flags.FlagAnnotationTest#test1: ASSUMPTION_FAILED (10ms) [2/2] com.cts.flags.FlagAnnotationTest#test2: PASSED (2ms)
Sinon, le résultat du test est le suivant:
[1/2] com.cts.flags.FlagAnnotationTest#test1: PASSED (2ms) [2/2] com.cts.flags.FlagAnnotationTest#test2: ASSUMPTION_FAILED (10ms)
Valeurs par défaut de l'appareil
Le SetFlagsRule
initialisé utilise les valeurs d'indicateur de l'appareil. Si la valeur de l'indicateur sur l'appareil n'est pas remplacée, par exemple avec adb, la valeur par défaut est identique à la configuration de version de la compilation. Si la valeur de remplacement a été remplacée sur l'appareil, SetFlagsRule
utilise la valeur de remplacement comme valeur par défaut.
Si le même test est exécuté dans différentes configurations de version, la valeur des indicateurs non définis explicitement avec SetFlagsRule
peut varier.
Après chaque test, SetFlagsRule
rétablit l'instance FeatureFlags
dans Flags
à son FeatureFlagsImpl
d'origine, de sorte qu'elle n'ait pas d'effets secondaires sur d'autres méthodes et classes de test.