Con l'introduzione dei flag di lancio delle funzionalità, sono state introdotte nuove norme di test che devi rispettare:
- I test devono coprire sia i comportamenti attivati che disattivati del flag.
- Durante il test, devi utilizzare i meccanismi ufficiali per impostare i valori dei flag.
- I test xTS non devono sostituire i valori dei flag nei test.
La sezione successiva fornisce i meccanismi ufficiali che devi utilizzare per rispettare queste norme.
Testare il codice segnalato
| Scenario di test | Meccanismo utilizzato |
|---|---|
| Test locale quando i valori dei flag cambiano spesso | Android Debug Bridge come descritto in Modificare il valore di un flag in fase di runtime |
| Test locale quando i valori dei flag non cambiano spesso | File dei valori dei flag come descritto in Impostare i valori dei flag di lancio delle funzionalità |
| Test end-to-end in cui i valori dei flag cambiano | FeatureFlagTargetPreparer come descritto in Creare test end-to-end |
| Test delle unità in cui cambiano i valori dei flag | SetFlagsRule con @EnableFlags e @DisableFlags come descritto in Creare unit test (Java e Kotlin) o
Creare unit test (C e C++) |
| Test end-to-end o unit test in cui i valori dei flag non possono cambiare | CheckFlagsRule come descritto in Crea test end-to-end o unit test in cui i valori dei flag non cambiano |
Creare test end-to-end
AOSP fornisce una classe chiamata FeatureFlagTargetPreparer, che consente
test end-to-end su un dispositivo. Questa classe accetta gli override dei valori dei flag come input, imposta questi flag nella configurazione dei dispositivi prima dell'esecuzione del test e li ripristina dopo l'esecuzione.
Puoi applicare la funzionalità della classe FeatureFlagTargetPreparer a livello di modulo di test e configurazione di test.
Applica FeatureFlagTargetPreparer in una configurazione del modulo di test
Per applicare FeatureFlagTargetPreparer in una configurazione del modulo di test, includi
FeatureFlagTargetPreparer e gli override dei valori dei flag nel file di configurazione del modulo di 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>
Dove:
target.preparer classè sempre impostato sucom.android.tradefed.targetprep.FeatureFlagTargetPreparer.optionè l'override del flag connamesempre impostato suflag-valueevalueimpostato sunamespace/aconfigPackage.flagName=true|false.
Crea moduli di test parametrizzati in base agli stati dei flag
Per creare moduli di test parametrizzati in base agli stati dei flag:
Includi
FeatureFlagTargetPreparernel file di configurazione del modulo di testAndroidTest.xml:<target_preparer class="com.android.tradefed.targetprep.FeatureFlagTargetPreparer" >Specifica le opzioni del valore del flag nella sezione
test_module_configdi un file di buildAndroid.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"}, ], }Il campo
optionscontiene gli override dei flag connamesempre impostato suflag-valueevalueimpostato sunamespace/aconfigPackage.flagName=true|false.
Crea test delle unità (Java e Kotlin)
Questa sezione descrive l'approccio all'override dei valori dei flag di aconfig a livello di classe e metodo (per test) nei test Java e Kotlin.
Per scrivere test delle unità automatizzati in una base di codice di grandi dimensioni con un numero elevato di flag, segui questi passaggi:
- Utilizza la classe
SetFlagsRulecon le annotazioni@EnableFlagse@DisableFlagsper testare tutti i rami di codice. - Utilizza il metodo
SetFlagsRule.ClassRuleper evitare bug di test comuni. - Utilizza
FlagsParameterizationper testare le tue classi in un'ampia gamma di configurazioni di flag.
Testare tutti i rami del codice
Per i progetti che utilizzano la classe statica per accedere ai flag, viene fornita la classe helper SetFlagsRule per sostituire i valori dei flag. Il seguente
snippet di codice mostra come includere SetFlagsRule e abilitare più flag
contemporaneamente:
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() {
...
}
Dove:
@Ruleè un'annotazione utilizzata per aggiungere la dipendenza flag-JUnit della classeSetFlagsRule.SetFlagsRuleè una classe helper fornita per sostituire i valori dei flag. Per informazioni su comeSetFlagsRuledetermina i valori predefiniti, consulta Valori predefiniti del dispositivo.@EnableFlagsè un'annotazione che accetta un numero arbitrario di nomi di flag. Quando disattivi i flag, utilizza@DisableFlags. Puoi applicare queste annotazioni a un metodo o a una classe.
Imposta i valori dei flag per l'intero processo di test, a partire da
SetFlagsRule, che precede qualsiasi metodo di configurazione
annotato con @Before nel test. I valori dei flag tornano allo stato precedente al termine di
SetFlagsRule, ovvero dopo qualsiasi metodo di configurazione annotato con @After.
Assicurati che i flag siano impostati correttamente
Come accennato in precedenza, SetFlagsRule viene utilizzato con l'annotazione JUnit @Rule, il che significa che SetFlagsRule non può garantire che i flag siano impostati correttamente durante il costruttore della classe di test o qualsiasi metodo annotato con @BeforeClass o @AfterClass.
Per assicurarti che i test fixture vengano creati con il valore di classe corretto, utilizza
il metodo SetFlagsRule.ClassRule in modo che i fixture non vengano
creati finché non viene chiamato un metodo di configurazione annotato 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() {
...
}
}
Se aggiungi la regola della classe SetFlagsRule.ClassRule, test_flag_foo_turned_on
non viene eseguito prima dell'esecuzione quando FLAG_FLAG_FOO viene letto dal costruttore di
DemoClass.
Se è necessario attivare un flag per l'intera classe, sposta l'annotazione @EnableFlags a livello di classe (prima della dichiarazione della classe). Lo spostamento dell'annotazione a livello di classe consente a SetFlagsRule.ClassRule di garantire che il flag sia impostato correttamente durante il costruttore della classe di test o durante qualsiasi metodo annotato con @BeforeClass o @AfterClass.
Eseguire test su più configurazioni di flag
Poiché puoi impostare i valori dei flag in base al test, puoi anche utilizzare la parametrizzazione per eseguire test su più configurazioni di flag:
...
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() {...}
}
Tieni presente che con SetFlagsRule, ma senza parametrizzazione, questa classe esegue
tre
test (fooLogic, legacyBarLogic e newBarLogic). Il metodo fooLogic
viene eseguito con i valori di FLAG_FOO e FLAG_BAR impostati sul
dispositivo.
Quando viene aggiunta la parametrizzazione, il metodo FlagsParameterization.allCombinationsOf
crea tutte le combinazioni possibili dei flag FLAG_FOO e FLAG_BAR:
FLAG_FOOètrueeFLAG_BARètrueFLAG_FOOètrueeFLAG_BARèfalseFLAG_FOOèfalseeFLAG_BARètrueFLAG_FOOè false eFLAG_BARèfalse
Anziché modificare direttamente i valori dei flag, le annotazioni @DisableFlags e
@EnableFlags modificano i valori dei flag in base alle condizioni dei parametri. Ad esempio, legacyBarLogic viene eseguito solo quando FLAG_BAR è disattivato, il che si verifica in due delle quattro combinazioni di flag. Il legacyBarLogic viene saltato per le altre
due combinazioni.
Esistono due metodi per creare le parametrizzazioni per i flag:
FlagsParameterization.allCombinationsOf(String...)esegue 2^n esecuzioni di ogni test. Ad esempio, un flag esegue test 2x o quattro flag eseguono test 16x.FlagsParameterization.progressionOf(String...)esegue n+1 esecuzioni di ogni test. Ad esempio, un flag esegue test 2x e quattro flag eseguono test 5x.
Crea test delle unità (C e C++)
AOSP include macro di valori di flag per i test C e C++ scritti nel framework GoogleTest.
Nella sorgente di test, includi le definizioni delle macro e le librerie generate da aconfig:
#include <flag_macros.h> #include "android_cts_flags.h"Nell'origine di test, anziché utilizzare le macro
TESTeTESTFper i casi di test, utilizzaTEST_WITH_FLAGSeTEST_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"); }Dove:
- Vengono utilizzate le macro
TEST_WITH_FLAGSeTEST_F_WITH_FLAGSanziché le macroTESTeTEST_F. REQUIRES_FLAGS_ENABLEDdefinisce un insieme di flag di rilascio delle funzionalità che devono soddisfare la condizione di attivazione. Puoi scrivere questi flag nelle macroACONFIG_FLAGoLEGACY_FLAG.REQUIRES_FLAGS_DISABLEDdefinisce un insieme di feature flag che devono soddisfare la condizione di disattivazione. Puoi scrivere questi flag nelle macroACONFIG_FLAGoLEGACY_FLAG.ACONFIG_FLAG (TEST_NS, readwrite_enabled_flag)è una macro utilizzata per i flag definiti nei file di configurazione. Questa macro accetta uno spazio dei nomi (TEST_NS) e un nome flag (readwrite_enabled_flag).LEGACY_FLAG(aconfig_flags.cts, TEST_NS, readwrite_disabled_flag)è una macro utilizzata per i flag impostati per impostazione predefinita nella configurazione del dispositivo.
- Vengono utilizzate le macro
Nel file di build
Android.bp, aggiungi le librerie generate da aconfig e le librerie di macro pertinenti come dipendenza di 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"], ... }Esegui i test in locale con questo comando:
atest FlagMacrosTestsSe il flag
my_namespace.android.myflag.tests.my_flagè disattivato, il risultato del test è:[1/2] MyTest#test1: IGNORED (0ms) [2/2] MyTestF#test2: PASSED (0ms)Se il flag
my_namespace.android.myflag.tests.my_flagè attivato, il risultato del test è:[1/2] MyTest#test1: PASSED (0ms) [2/2] MyTestF#test2: IGNORED (0ms)
Crea test end-to-end o unit test in cui i valori dei flag non cambiano
Per gli scenari di test in cui non puoi eseguire l'override dei flag e puoi filtrare i test solo se
si basano sullo stato attuale del flag, utilizza la regola CheckFlagsRule con
le annotazioni RequiresFlagsEnabled e RequiresFlagsDisabled.
I seguenti passaggi mostrano come creare ed eseguire un test end-to-end o unitario in cui i valori dei flag non possono essere sostituiti:
Nel codice di test, utilizza
CheckFlagsRuleper applicare il filtro dei test. Inoltre, utilizza le annotazioni JavaRequiresFlagsEnabledeRequiredFlagsDisabledper specificare i requisiti dei flag per il test.Il test lato dispositivo utilizza 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() {} }Il test lato host utilizza 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() {} }Aggiungi
jflag-unite le librerie generate da aconfig alla sezionestatic_libsdel file di build per il test:android_test { name: "FlagAnnotationTests", srcs: ["*.java"], static_libs: [ "androidx.test.rules", "my_aconfig_lib", "flag-junit", "platform-test-annotations", ], test_suites: ["general-tests"], }Utilizza il comando seguente per eseguire il test in locale:
atest FlagAnnotationTestsSe il flag
Flags.FLAG_FLAG_NAME_1è disattivato, il risultato del test è:[1/2] com.cts.flags.FlagAnnotationTest#test1: ASSUMPTION_FAILED (10ms) [2/2] com.cts.flags.FlagAnnotationTest#test2: PASSED (2ms)In caso contrario, il risultato del test è:
[1/2] com.cts.flags.FlagAnnotationTest#test1: PASSED (2ms) [2/2] com.cts.flags.FlagAnnotationTest#test2: ASSUMPTION_FAILED (10ms)
Valori predefiniti del dispositivo
SetFlagsRule inizializzato utilizza i valori dei flag del dispositivo. Se il valore del flag sul dispositivo non viene sostituito, ad esempio con adb, il valore predefinito è uguale alla configurazione di rilascio della build. Se il valore sul dispositivo è stato
sostituito, SetFlagsRule utilizza il valore di sostituzione come
valore predefinito.
Se lo stesso test viene eseguito con configurazioni di rilascio diverse, il
valore dei flag non impostati in modo esplicito con SetFlagsRule può variare.
Dopo ogni test, SetFlagsRule ripristina l'istanza FeatureFlags in Flags
al suo FeatureFlagsImpl originale, in modo che non abbia effetti collaterali su
altri metodi e classi di test.