Mit der Einführung von Flags für die Einführung von Funktionen gelten neue Testanforderungen, die Sie einhalten müssen:
- Ihre Tests müssen sowohl das Verhalten bei aktivierter als auch bei deaktivierter Markierung abdecken.
- Sie müssen die offiziellen Mechanismen verwenden, um während der Tests Flag-Werte festzulegen.
- In xTS-Tests sollten keine Flag-Werte überschrieben werden.
Im nächsten Abschnitt werden die offiziellen Mechanismen beschrieben, die Sie zur Einhaltung dieser Richtlinien verwenden müssen.
Code mit Markierungen testen
Testszenario | Verwendeter Mechanismus |
---|---|
Lokale Tests, wenn sich Flaggenwerte häufig ändern | Android Debug Bridge wie unter Wert eines Flags zur Laufzeit ändern beschrieben |
Lokale Tests, wenn sich Flag-Werte nicht oft ändern | Flag-Wertdatei wie unter Werte für das Flag zur Produktveröffentlichung festlegen beschrieben |
End-to-End-Tests, bei denen sich Flag-Werte ändern | FeatureFlagTargetPreparer wie im Abschnitt End-to-End-Tests erstellen beschrieben |
Unittests, bei denen sich Flag-Werte ändern | SetFlagsRule mit @EnableFlags und @DisableFlags , wie unter Einheitstests erstellen (Java und Kotlin) oder Einheitstests erstellen (C und C++) beschrieben |
Ende-zu-Ende- oder Unit-Tests, bei denen sich Flag-Werte nicht ändern können | CheckFlagsRule wie unter Ende-zu-Ende- oder Unit-Tests erstellen, bei 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, setzt diese Flags vor der Testausführung in der Gerätekonfiguration und stellt sie nach der Ausführung wieder her.
Sie können die Funktionen der Klasse FeatureFlagTargetPreparer
auf Testmodul- und Testkonfigurationsebene anwenden.
FeatureFlagTargetPreparer in einer Testmodulkonfiguration anwenden
Wenn Sie FeatureFlagTargetPreparer
in einer Testmodulkonfiguration anwenden möchten, fügen Sie der Konfigurationsdatei des Testmoduls AndroidTest.xml
Überschreibungen für FeatureFlagTargetPreparer
und Flag-Werte hinzu:
<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 class
ist immer aufcom.android.tradefed.targetprep.FeatureFlagTargetPreparer
festgelegt.option
ist die Flag-Überschreibung, wobeiname
immer aufflag-value
undvalue
aufnamespace/aconfigPackage.flagName=true|false
festgelegt ist.
Parameterisierte Testmodule basierend auf Flag-Status erstellen
So erstellen Sie parametrisierte Testmodule basierend auf Flag-Status:
Fügen Sie
FeatureFlagTargetPreparer
in die Konfigurationsdatei desAndroidTest.xml
-Testmoduls ein:<target_preparer class="com.android.tradefed.targetprep.FeatureFlagTargetPreparer" >
Geben Sie die Optionen für den Flag-Wert im Abschnitt
test_module_config
einerAndroid.bp
-Builddatei 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
options
enthält die Flag-Überschreibungen.name
ist immer aufflag-value
undvalue
aufnamespace/aconfigPackage.flagName=true|false
gesetzt.
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
SetFlagsRule
mit den Anmerkungen@EnableFlags
und@DisableFlags
, um alle Codezweige zu testen. - Verwenden Sie die Methode
SetFlagsRule.ClassRule
, um häufige Testfehler zu vermeiden. - Mit
FlagsParameterization
können Sie Ihre Klassen mit einer Vielzahl von Flag-Konfigurationen testen.
Alle Codezweige testen
Bei Projekten, in denen die statische Klasse zum Zugriff auf Flags verwendet 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:
@Rule
ist eine Annotation, mit der die Flag-JUnit-Abhängigkeit der KlasseSetFlagsRule
hinzugefügt wird.SetFlagsRule
ist eine Hilfsklasse, mit der Flag-Werte überschrieben werden können. Informationen dazu, wieSetFlagsRule
Standardwerte ermittelt, finden Sie unter Gerätestandardwerte.@EnableFlags
ist eine Anmerkung, die eine beliebige Anzahl von Flaggennamen akzeptiert. Verwenden Sie@DisableFlags
, um Flags zu deaktivieren. Sie können diese Anmerkungen entweder auf eine Methode oder eine Klasse anwenden.
Legen Sie Flag-Werte für den gesamten Testprozess fest, beginnend mit SetFlagsRule
, was vor allen mit @Before
annotierten Einrichtungsmethoden im Test liegt. Die Flag-Werte werden wieder auf ihren vorherigen Zustand zurückgesetzt, wenn SetFlagsRule
abgeschlossen ist. Das ist nach allen mit @After
annotierten Einrichtungsmethoden der Fall.
Prüfen, ob die Flags richtig gesetzt sind
Wie bereits erwähnt, wird SetFlagsRule
mit der JUnit-@Rule
-Anmerkung verwendet. Das bedeutet, dass SetFlagsRule
nicht dafür sorgen kann, dass Ihre Flags während des Konstruktors der Testklasse oder in Methoden mit der Anmerkung @BeforeClass
oder @AfterClass
richtig festgelegt werden.
Damit Test-Fixtures mit dem richtigen Klassenwert erstellt werden, verwenden Sie die Methode SetFlagsRule.ClassRule
, damit Ihre Fixtures erst erstellt werden, wenn eine mit @Before
annotierte Einrichtungsmethode aufgerufen wird:
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() {
...
}
}
Wenn Sie die SetFlagsRule.ClassRule
-Klassenregel hinzufügen, schlägt test_flag_foo_turned_on
fehl, bevor es ausgeführt wird, wenn FLAG_FLAG_FOO
vom Konstruktor von DemoClass
gelesen wird.
Wenn für den gesamten Kurs ein Flag aktiviert werden muss, verschieben Sie die @EnableFlags
-Anmerkung auf Kursebene (vor die Kursdeklaration). Durch das Verschieben der Anmerkung auf Klassenebene kann SetFlagsRule.ClassRule
dafür sorgen, dass das Flag im Konstruktor der Testklasse oder in allen mit @BeforeClass
oder @AfterClass
annotierten Methoden korrekt festgelegt wird.
Tests für mehrere Flag-Konfigurationen ausführen
Da Sie Flag-Werte pro Test festlegen können, können Sie mithilfe der Parameterisierung auch Tests für mehrere Flag-Konfigurationen ausfü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() {...}
}
Hinweis: Mit SetFlagsRule
, aber ohne Parameter werden in dieser Klasse drei Tests ausgeführt (fooLogic
, legacyBarLogic
und newBarLogic
). Die Methode fooLogic
wird mit den Werten von FLAG_FOO
und FLAG_BAR
ausgeführt, die auf dem Gerät festgelegt sind.
Wenn eine Parameterisierung hinzugefügt wird, werden mit der FlagsParameterization.allCombinationsOf
-Methode alle möglichen Kombinationen der Flags FLAG_FOO
und FLAG_BAR
erstellt:
FLAG_FOO
isttrue
undFLAG_BAR
isttrue
FLAG_FOO
isttrue
undFLAG_BAR
istfalse
FLAG_FOO
istfalse
undFLAG_BAR
isttrue
FLAG_FOO
ist falsch undFLAG_BAR
istfalse
Anstatt Flag-Werte direkt zu ändern, ändern Anmerkungen vom Typ @DisableFlags
und @EnableFlags
Flag-Werte basierend auf Parameterbedingungen. Beispiel: legacyBarLogic
wird nur ausgeführt, wenn FLAG_BAR
deaktiviert ist. Das ist bei zwei der vier Flag-Kombinationen der Fall. Bei den anderen beiden Kombinationen wird legacyBarLogic
übersprungen.
Es gibt zwei Methoden zum Erstellen der Parameterisierungen für Ihre Flags:
FlagsParameterization.allCombinationsOf(String...)
führt 2 hoch n Ausführungen jedes Tests aus. Mit einer Flagge werden beispielsweise zwei Tests ausgeführt, mit vier Flags 16 Tests.FlagsParameterization.progressionOf(String...)
führt n + 1 Ausführungen jedes Tests aus. Beispiel: Mit einem Flag werden doppelt so viele Tests ausgeführt wie mit vier Flags.
Unittests erstellen (C und C++)
AOSP enthält Flag-Wert-Makros für C- und C++-Tests, die im GoogleTest-Framework geschrieben wurden.
.Fügen Sie in Ihrer 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
TEST
undTESTF
für Ihre TestfälleTEST_WITH_FLAGS
undTEST_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:
- Anstelle von
TEST
- undTEST_F
-Makros werdenTEST_WITH_FLAGS
- undTEST_F_WITH_FLAGS
-Makros verwendet. REQUIRES_FLAGS_ENABLED
definiert eine Reihe von Flags für die Funktion, die die Aktivierungsbedingung erfüllen müssen. Sie können diese Flags inACONFIG_FLAG
- oderLEGACY_FLAG
-Makros schreiben.- Mit
REQUIRES_FLAGS_DISABLED
werden eine Reihe von Feature-Flags definiert, die die Bedingung „deaktiviert“ 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 in Konfigurationsdateien definierte Flags verwendet wird. 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, das für Flags verwendet wird, die standardmäßig in der Gerätekonfiguration festgelegt sind.
- Anstelle von
Fügen Sie in Ihrer
Android.bp
-Builddatei die von aconfig generierten Bibliotheken und die 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 dem folgenden Befehl lokal aus:
atest FlagMacrosTests
Wenn das Flag
my_namespace.android.myflag.tests.my_flag
deaktiviert ist, ist das Testergebnis:[1/2] MyTest#test1: IGNORED (0ms) [2/2] MyTestF#test2: PASSED (0ms)
Wenn das Flag
my_namespace.android.myflag.tests.my_flag
aktiviert ist, ist das Testergebnis:[1/2] MyTest#test1: PASSED (0ms) [2/2] MyTestF#test2: IGNORED (0ms)
End-to-End- oder Unit-Tests erstellen, bei denen sich Flag-Werte nicht ändern
Bei Testfällen, 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 beschrieben, wie Sie einen End-to-End- oder Unit-Test erstellen und ausführen, bei dem Flag-Werte nicht überschrieben werden können:
Verwenden Sie in Ihrem Testcode
CheckFlagsRule
, um die Testfilterung anzuwenden. Verwenden Sie außerdem die Java-AnmerkungenRequiresFlagsEnabled
undRequiredFlagsDisabled
, um die Flag-Anforderungen für Ihren Test anzugeben.Für den geräteseitigen Test wird die Klasse
DeviceFlagsValueProvider
verwendet:@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 hostseitigen Test wird die Klasse
HostFlagsValueProvider
verwendet:@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 der Build-Datei für Ihren Test im Abschnitt
static_libs
jflag-unit
- und aconfig-generierte Bibliotheken 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 FlagAnnotationTests
Wenn das Flag
Flags.FLAG_FLAG_NAME_1
deaktiviert ist, ist das Testergebnis:[1/2] com.cts.flags.FlagAnnotationTest#test1: ASSUMPTION_FAILED (10ms) [2/2] com.cts.flags.FlagAnnotationTest#test2: PASSED (2ms)
Andernfalls ist das Testergebnis:
[1/2] com.cts.flags.FlagAnnotationTest#test1: PASSED (2ms) [2/2] com.cts.flags.FlagAnnotationTest#test2: ASSUMPTION_FAILED (10ms)
Gerätestandardwerte
Die 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, verwendet SetFlagsRule
den Überschreibungswert als Standard.
Wenn derselbe Test unter 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
wieder auf ihren ursprünglichen FeatureFlagsImpl
zurück, damit es keine Nebenwirkungen auf andere Testmethoden und ‑klassen hat.