Mit der Einführung von Flags für die Einführung von Funktionen gibt es neue Testrichtlinien, die Sie einhalten müssen:
- Ihre Tests müssen sowohl das Verhalten des Flags bei Aktivierung als auch bei Deaktivierung abdecken.
- Sie müssen die offiziellen Mechanismen verwenden, um Flag-Werte während des Tests festzulegen.
- xTS-Tests sollten keine Flag-Werte in Tests überschreiben.
Im nächsten Abschnitt werden die offiziellen Mechanismen beschrieben, die Sie verwenden müssen, um diese Richtlinien einzuhalten.
Gekennzeichneten Code testen
| Testszenario | Verwendeter Mechanismus |
|---|---|
| Lokale Tests bei häufigen Änderungen von Flag-Werten | Android Debug Bridge, wie unter Flag-Wert zur Laufzeit ändern beschrieben |
| Lokale Tests, wenn sich Flag-Werte nicht oft ändern | Datei mit Flag-Werten, wie unter Werte für das Flag für die Einführung von Funktionen festlegen beschrieben |
| End-to-End-Tests, bei denen sich Flag-Werte ändern | FeatureFlagTargetPreparer, wie unter End-to-End-Tests erstellen beschrieben |
| Unittests, bei denen sich Flag-Werte ändern | SetFlagsRule mit @EnableFlags und @DisableFlags, wie unter Einheitentests erstellen (Java und Kotlin) oder Einheitentests erstellen (C und C++) beschrieben |
| Ende-zu-Ende- oder Unit-Tests, bei denen sich die Flag-Werte nicht ändern können | CheckFlagsRule, wie unter Ende-zu-Ende- oder Unittests erstellen, in denen sich Flag-Werte nicht ändern beschrieben |
End-to-End-Tests erstellen
AOSP bietet eine Klasse namens FeatureFlagTargetPreparer, die End-to-End-Tests auf einem Gerät ermöglicht. Diese Klasse akzeptiert Überschreibungen von Flag-Werten als Eingabe, legt diese Flags in der Gerätekonfiguration vor der Testausführung fest und stellt Flags nach der Ausführung wieder her.
Sie können die Funktionalität der Klasse FeatureFlagTargetPreparer auf Testmodulebene und Testkonfigurationsebene anwenden.
FeatureFlagTargetPreparer in einer Testmodulkonfiguration anwenden
Wenn Sie FeatureFlagTargetPreparer in einer Testmodulkonfiguration anwenden möchten, fügen Sie FeatureFlagTargetPreparer und Überschreibungen für Flag-Werte in die Konfigurationsdatei des Testmoduls AndroidTest.xml ein:
<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>
Dabei gilt:
target.preparer classist immer aufcom.android.tradefed.targetprep.FeatureFlagTargetPreparerfestgelegt.optionist die Flag-Überschreibung, wobeinameimmer aufflag-valueundvalueaufnamespace/aconfigPackage.flagName=true|falsefestgelegt ist.
Parametrisierte Testmodule basierend auf Flag-Status erstellen
So erstellen Sie parametrisierte Testmodule basierend auf Flag-Status:
Fügen Sie
FeatureFlagTargetPreparerin die Konfigurationsdatei des TestmodulsAndroidTest.xmlein:<target_preparer class="com.android.tradefed.targetprep.FeatureFlagTargetPreparer" >Geben Sie die Optionen für den Flag-Wert im Abschnitt
test_module_configeinerAndroid.bp-Build-Datei an: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"}, ], }Das Feld
optionsenthält die Flag-Überschreibungen, wobeinameimmer aufflag-valueundvalueaufnamespace/aconfigPackage.flagName=true|falsegesetzt ist.
Einheitentests erstellen (Java und Kotlin)
In diesem Abschnitt wird beschrieben, wie Sie aconfig-Flag-Werte in Java- und Kotlin-Tests auf Klassen- und Methodenebene (pro Test) überschreiben.
So schreiben Sie automatisierte Unit-Tests in einer großen Codebasis mit einer großen Anzahl von Flags:
- Verwenden Sie die Klasse
SetFlagsRulemit den Annotationen@EnableFlagsund@DisableFlags, um alle Codezweige zu testen. - Verwenden Sie die Methode
SetFlagsRule.ClassRule, um häufige Testfehler zu vermeiden. - Verwenden Sie
FlagsParameterization, um Ihre Klassen mit einer Vielzahl von Flag-Konfigurationen zu testen.
Alle Codezweige testen
Bei Projekten, in denen über die statische Klasse auf Flags zugegriffen wird, wird die Hilfsklasse SetFlagsRule bereitgestellt, um Flag-Werte zu überschreiben. Das folgende Code-Snippet zeigt, wie Sie SetFlagsRule einfügen und mehrere Flags gleichzeitig aktivieren:
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() {
...
}
Dabei gilt:
@Ruleist eine Annotation, mit der die Flag-JUnit-Abhängigkeit der KlasseSetFlagsRulehinzugefügt wird.SetFlagsRuleist eine Hilfsklasse zum Überschreiben von Flag-Werten. Informationen dazu, wieSetFlagsRuleStandardwerte ermittelt, finden Sie unter Standardwerte für Geräte.@EnableFlagsist eine Anmerkung, die eine beliebige Anzahl von Flag-Namen akzeptiert. Verwenden Sie@DisableFlags, um Flags zu deaktivieren. Sie können diese Anmerkungen auf eine Methode oder eine Klasse anwenden.
Legen Sie Flag-Werte für den gesamten Testprozess fest. Beginnen Sie mit SetFlagsRule, das vor allen @Before-annotated-Einrichtungsmethoden im Test steht. Die Flag-Werte werden nach Abschluss von SetFlagsRule auf ihren vorherigen Zustand zurückgesetzt. Das ist nach allen Einrichtungsmethoden, die mit @After annotiert sind.
Prüfen, ob die Flags richtig gesetzt sind
Wie bereits erwähnt, wird SetFlagsRule mit der JUnit-Annotation @Rule verwendet. Das bedeutet, dass SetFlagsRule nicht dafür sorgen kann, dass Ihre Flags während des Konstruktors der Testklasse oder einer Methode mit der Annotation @BeforeClass oder @AfterClass richtig festgelegt werden.
Verwenden Sie die Methode SetFlagsRule.ClassRule, damit Test-Fixtures erst erstellt werden, wenn eine mit @Before annotierte Einrichtungsmethode aufgerufen wird. So wird sichergestellt, dass Test-Fixtures mit dem richtigen Klassenwert erstellt werden:
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() {
...
}
}
Durch das Hinzufügen der Klassenregel SetFlagsRule.ClassRule schlägt test_flag_foo_turned_on vor der Ausführung fehl, wenn FLAG_FLAG_FOO vom Konstruktor von DemoClass gelesen wird.
Wenn für die gesamte Klasse ein Flag aktiviert werden muss, verschieben Sie die Annotation @EnableFlags auf Klassenebene (vor die Klassendeklaration). Durch das Verschieben der Annotation auf Klassenebene kann SetFlagsRule.ClassRule dafür sorgen, dass das Flag im Konstruktor der Testklasse oder in Methoden, die mit @BeforeClass oder @AfterClass annotiert sind, richtig gesetzt wird.
Tests mit mehreren Flag-Konfigurationen ausführen
Da Sie Flag-Werte für jeden Test einzeln festlegen können, können Sie auch die Parametrisierung verwenden, um Tests mit mehreren Flag-Konfigurationen auszuführen:
...
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() {...}
}
Bei SetFlagsRule, aber ohne Parametrisierung, werden in dieser Klasse drei Tests ausgeführt (fooLogic, legacyBarLogic und newBarLogic). Die Methode fooLogic wird mit den Werten für FLAG_FOO und FLAG_BAR ausgeführt, die auf dem Gerät festgelegt sind.
Wenn die Parametrisierung hinzugefügt wird, erstellt die Methode FlagsParameterization.allCombinationsOf alle möglichen Kombinationen der Flags FLAG_FOO und FLAG_BAR:
FLAG_FOOisttrueundFLAG_BARisttrueFLAG_FOOisttrueundFLAG_BARistfalseFLAG_FOOistfalseundFLAG_BARisttrueFLAG_FOOist falsch undFLAG_BARistfalse
Anstatt Flag-Werte direkt zu ändern, werden mit den Anmerkungen @DisableFlags und @EnableFlags Flag-Werte basierend auf Parameterbedingungen geändert. legacyBarLogic wird beispielsweise nur ausgeführt, wenn FLAG_BAR deaktiviert ist. Das ist bei zwei der vier Flag-Kombinationen der Fall. Die legacyBarLogic wird für die anderen beiden Kombinationen übersprungen.
Es gibt zwei Methoden zum Erstellen der Parameterisierungen für Ihre Flags:
FlagsParameterization.allCombinationsOf(String...)führt 2^n Ausführungen jedes Tests aus. Mit einem Flag werden beispielsweise 2 Tests ausgeführt, mit vier Flags 16 Tests.FlagsParameterization.progressionOf(String...)führt n+1 Ausführungen jedes Tests aus. Beispiel: Ein Flag führt 2 Tests aus, vier Flags führen 5 Tests aus.
Unittests erstellen (C und C++)
AOSP enthält Makros für Flag-Werte für C- und C++-Tests, die im GoogleTest-Framework geschrieben wurden.
Fügen Sie in Ihre Testquelle die Makrodefinitionen und die von aconfig generierten Bibliotheken ein:
#include <flag_macros.h> #include "android_cts_flags.h"Verwenden Sie in Ihrer Testquelle anstelle der Makros
TESTundTESTFfür Ihre TestläufeTEST_WITH_FLAGSundTEST_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"); }Dabei gilt:
- Statt der Makros
TESTundTEST_Fwerden die MakrosTEST_WITH_FLAGSundTEST_F_WITH_FLAGSverwendet. REQUIRES_FLAGS_ENABLEDdefiniert eine Reihe von Flags für die Veröffentlichung von Funktionen, die die aktivierte Bedingung erfüllen müssen. Sie können diese Flags inACONFIG_FLAG- oderLEGACY_FLAG-Makros schreiben.REQUIRES_FLAGS_DISABLEDdefiniert eine Reihe von Feature-Flags, die die Bedingung „disabled“ erfüllen müssen. Sie können diese Flags inACONFIG_FLAG- oderLEGACY_FLAG-Makros schreiben.ACONFIG_FLAG (TEST_NS, readwrite_enabled_flag)ist ein Makro, das für Flags verwendet wird, die in Konfigurationsdateien definiert sind. Dieses Makro akzeptiert einen Namespace (TEST_NS) und einen Flag-Namen (readwrite_enabled_flag).LEGACY_FLAG(aconfig_flags.cts, TEST_NS, readwrite_disabled_flag)ist ein Makro für Flags, die standardmäßig in der Gerätekonfiguration festgelegt sind.
- Statt der Makros
Fügen Sie in Ihrer
Android.bp-Build-Datei die aconfig-generierten Bibliotheken und relevanten Makrobibliotheken als Testabhängigkeit hinzu: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"], ... }Führen Sie die Tests mit diesem Befehl lokal aus:
atest FlagMacrosTestsWenn das Flag
my_namespace.android.myflag.tests.my_flagdeaktiviert ist, lautet das Testergebnis:[1/2] MyTest#test1: IGNORED (0ms) [2/2] MyTestF#test2: PASSED (0ms)Wenn das Flag
my_namespace.android.myflag.tests.my_flagaktiviert ist, lautet das Testergebnis:[1/2] MyTest#test1: PASSED (0ms) [2/2] MyTestF#test2: IGNORED (0ms)
End-to-End- oder Einheitentests erstellen, bei denen sich die Flag-Werte nicht ändern
Für Testläufe, bei denen Sie Flags nicht überschreiben und Tests nur filtern können, wenn sie auf dem aktuellen Flag-Status basieren, verwenden Sie die Regel CheckFlagsRule mit den Anmerkungen RequiresFlagsEnabled und RequiresFlagsDisabled.
In den folgenden Schritten wird gezeigt, wie Sie einen End-to-End- oder Unittest erstellen und ausführen, in dem Flag-Werte nicht überschrieben werden können:
Verwenden Sie in Ihrem Testcode
CheckFlagsRule, um Testfilterung anzuwenden. Verwenden Sie außerdem die Java-AnnotationenRequiresFlagsEnabledundRequiredFlagsDisabled, um die Flag-Anforderungen für Ihren Test anzugeben.Für den geräteseitigen Test wird die Klasse
DeviceFlagsValueProviderverwendet:@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() {} }Für den Hostseitentest wird die Klasse
HostFlagsValueProviderverwendet:@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() {} }Fügen Sie
jflag-unitund aconfig-generierte Bibliotheken dem Abschnittstatic_libsder Build-Datei für Ihren Test hinzu:android_test { name: "FlagAnnotationTests", srcs: ["*.java"], static_libs: [ "androidx.test.rules", "my_aconfig_lib", "flag-junit", "platform-test-annotations", ], test_suites: ["general-tests"], }Verwenden Sie den folgenden Befehl, um den Test lokal auszuführen:
atest FlagAnnotationTestsWenn das Flag
Flags.FLAG_FLAG_NAME_1deaktiviert ist, lautet das Testergebnis:[1/2] com.cts.flags.FlagAnnotationTest#test1: ASSUMPTION_FAILED (10ms) [2/2] com.cts.flags.FlagAnnotationTest#test2: PASSED (2ms)Andernfalls lautet das Testergebnis:
[1/2] com.cts.flags.FlagAnnotationTest#test1: PASSED (2ms) [2/2] com.cts.flags.FlagAnnotationTest#test2: ASSUMPTION_FAILED (10ms)
Gerätestandardwerte
Das initialisierte SetFlagsRule verwendet Flag-Werte vom Gerät. Wenn der Flag-Wert auf dem Gerät nicht überschrieben wird, z. B. mit adb, entspricht der Standardwert der Release-Konfiguration des Builds. Wenn der Wert auf dem Gerät überschrieben wurde, wird der Überschreibungswert als Standardwert für SetFlagsRule verwendet.
Wenn derselbe Test mit verschiedenen Release-Konfigurationen ausgeführt wird, kann der Wert von Flags, die nicht explizit mit SetFlagsRule festgelegt wurden, variieren.
Nach jedem Test stellt SetFlagsRule die FeatureFlags-Instanz in Flags auf ihren ursprünglichen FeatureFlagsImpl-Wert zurück, damit sie keine Nebeneffekte auf andere Testmethoden und -klassen hat.