Con l'introduzione dei flag per il lancio delle funzionalità, ci sono nuovi criteri di test che devi rispettare:
- I test devono coprire i comportamenti sia abilitati che disattivati del flag.
- Devi utilizzare i meccanismi ufficiali per impostare i valori dei flag durante il test.
- I test xTS non devono sostituire i valori di flag nei test.
La sezione successiva illustra i meccanismi ufficiali che devi utilizzare per rispettare queste norme.
Testare il codice segnalato
Scenario del test | Meccanismo utilizzato |
---|---|
Test locali quando i valori degli indicatori cambiano spesso | Bridge di debug Android come discusso in Modificare il valore di un flag in fase di runtime |
Test locali quando i valori dei flag non cambiano spesso | File dei valori dei flag, come descritto in Impostare i valori dei flag di lancio della 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 del flag | SetFlagsRule con @EnableFlags e @DisableFlags , come descritto in Creare test delle unità (Java e Kotlin) o
Creare test delle unità (C e C++) |
Test end-to-end o di unità in cui i valori del flag non possono cambiare | CheckFlagsRule come descritto in Creare test end-to-end o unitari in cui i valori dei flag non cambiano |
Crea test end-to-end
AOSP fornisce una classe chiamata FeatureFlagTargetPreparer
, che consente di eseguire test end-to-end su un dispositivo. Questa classe accetta l'override dei valori dei flag come input, imposta i flag nella configurazione dei dispositivi prima dell'esecuzione del test e ripristina i flag dopo l'esecuzione.
Puoi applicare la funzionalità della classe FeatureFlagTargetPreparer
ai livelli di modulo di test e configurazione di test.
Applicare FeatureFlagTargetPreparer alla configurazione di un modulo di test
Per applicare FeatureFlagTargetPreparer
nella configurazione di un modulo di test, includi
FeatureFlagTargetPreparer
e l'override dei valori del 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 conname
sempre impostato suflag-value
evalue
impostato sunamespace/aconfigPackage.flagName=true|false
.
Creare moduli di test con parametri in base agli stati dei flag
Per creare moduli di test con parametri basati sugli stati dei flag:
Includi
FeatureFlagTargetPreparer
nel file di configurazione del modulo di testAndroidTest.xml
:<target_preparer class="com.android.tradefed.targetprep.FeatureFlagTargetPreparer" >
Specifica le opzioni dei valori di flag nella sezione
test_module_config
di 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
options
contiene gli override dei flag conname
sempre impostato suflag-value
evalue
impostato sunamespace/aconfigPackage.flagName=true|false
.
Creare test delle unità (Java e Kotlin)
Questa sezione descrive l'approccio per eseguire l'override dei valori del flag aconfig a livello di classe e metodo (per test) nei test Java e Kotlin.
Per scrivere test di unità automatici in una base di codice di grandi dimensioni con un numero elevato di flag:
- Utilizza la classe
SetFlagsRule
con le annotazioni@EnableFlags
e@DisableFlags
per testare tutti i rami di codice. - Utilizza il metodo
SetFlagsRule.ClassRule
per evitare i bug di test più comuni. - Utilizza
FlagsParameterization
per testare le tue classi in un ampio insieme di configurazioni di flag.
Testare tutti i rami di codice
Per i progetti che utilizzano la classe statica per accedere ai flag, viene fornita la classe di assistenza SetFlagsRule
per eseguire l'override dei valori dei flag. Il seguente snippet di codice mostra come includere SetFlagsRule
e attivare 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 eseguire l'override dei valori del flag. Per informazioni su comeSetFlagsRule
determina i valori predefiniti, consulta Valori predefiniti del dispositivo.@EnableFlags
è un'annotazione che accetta un numero arbitrario di nomi di indicatori. 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 tutti i metodi di configurazione annotati con @Before
nel test. I valori dei flag tornano allo stato precedente al termine dell'istruzione SetFlagsRule
, che segue qualsiasi metodo di configurazione annotato da @After
.
Assicurati che i flag siano impostati correttamente
Come accennato in precedenza, SetFlagsRule
viene utilizzato con l'annotazione @Rule
JUnit, 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 @BeforeClass
o @AfterClass
.
Per assicurarti che gli espositori di test siano costruiti con il valore della classe corretto, utilizza
il metodo SetFlagsRule.ClassRule
in modo che non vengano
creati fino a quando non viene indicato un metodo di configurazione annotato da @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 riesce prima di essere eseguito quando FLAG_FLAG_FOO
viene letto dal costruttore di DemoClass
.
Se è necessario attivare un flag per l'intero corso, sposta l'annotazione @EnableFlags
al livello di corso (prima della dichiarazione del corso). Spostare l'annotazione a livello di classe consente a SetFlagsRule.ClassRule
di assicurarsi che il flag sia impostato correttamente durante il costruttore della classe di test o durante i metodi con annotazione @BeforeClass
o @AfterClass
.
Esecuzione di test su più configurazioni di flag
Poiché puoi impostare i valori dei flag in base ai singoli 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 possibili combinazioni dei flag FLAG_FOO
e FLAG_BAR
:
FLAG_FOO
ètrue
eFLAG_BAR
ètrue
FLAG_FOO
ètrue
eFLAG_BAR
èfalse
FLAG_FOO
èfalse
eFLAG_BAR
ètrue
FLAG_FOO
è falso, mentreFLAG_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. legacyBarLogic
viene ignorato 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 doppi o quattro flag eseguono test 16 volte.FlagsParameterization.progressionOf(String...)
esegue n+1 esecuzioni di ogni test. Ad esempio, un flag esegue test 2x e quattro flag eseguono test 5x.
Crea i test delle unità (C e C++)
AOSP include le macro dei valori dei flag per i test C e C++ scritti nel framework GoogleTest.
Nella sorgente del test, includi le definizioni delle macro e le librerie generate da aconfig:
#include <flag_macros.h> #include "android_cts_flags.h"
Nell'origine del test, anziché utilizzare le macro
TEST
eTESTF
per i casi di test, utilizzaTEST_WITH_FLAGS
eTEST_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:
- Le macro
TEST_WITH_FLAGS
eTEST_F_WITH_FLAGS
vengono utilizzate al posto delle macroTEST
eTEST_F
. REQUIRES_FLAGS_ENABLED
definisce un insieme di flag di release delle funzionalità che devono soddisfare la condizione di attivazione. Puoi scrivere questi flag nelle macroACONFIG_FLAG
oLEGACY_FLAG
.REQUIRES_FLAGS_DISABLED
definisce un insieme di flag delle funzionalità che devono soddisfare la condizione disabilitata. Puoi scrivere questi flag nelle macroACONFIG_FLAG
oLEGACY_FLAG
.ACONFIG_FLAG (TEST_NS, readwrite_enabled_flag)
è una macro utilizzata per i flag definiti nei file di configurazione. Questa macro accetta un ambito (TEST_NS
) e un nome di indicatore (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 dei dispositivi.
- Le macro
Nel file di build
Android.bp
, aggiungi le librerie generate da aconfig e le librerie delle 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 FlagMacrosTests
Se il flag
my_namespace.android.myflag.tests.my_flag
è disabilitato, 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 unitari 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 di flag corrente, utilizza la regola CheckFlagsRule
con le annotazioni RequiresFlagsEnabled
e RequiresFlagsDisabled
.
I seguenti passaggi mostrano come creare ed eseguire un test end-to-end o di unità in cui non è possibile eseguire l'override dei valori del flag:
Nel codice di test, utilizza
CheckFlagsRule
per applicare il filtro dei test. Inoltre, utilizza le annotazioni JavaRequiresFlagsEnabled
eRequiredFlagsDisabled
per 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-unit
e le librerie generate da aconfig alla sezionestatic_libs
del file di build per il tuo 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 FlagAnnotationTests
Se il flag
Flags.FLAG_FLAG_NAME_1
viene 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 di flag del dispositivo. Se il valore del flag sul dispositivo non viene sostituito, ad esempio con adb, il valore predefinito corrisponde a quello della configurazione di release della build. Se è stato eseguito l'override del valore sul dispositivo, SetFlagsRule
utilizza il valore di override come predefinito.
Se lo stesso test viene eseguito in configurazioni di release diverse, il valore dei flag non impostati esplicitamente 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.