Mit der Einführung von Flags zur Einführung von Funktionen gibt es neue Testrichtlinien, die Sie einhalten müssen:
- Ihre Tests müssen sowohl das aktivierte als auch das deaktivierte Verhalten des Flags abdecken.
- Sie müssen die offiziellen Mechanismen verwenden, um während des Tests Flag-Werte festzulegen.
- In xTS-Tests sollten keine Flag-Werte überschrieben werden.
Der nächste Abschnitt enthält die offiziellen Mechanismen, mit denen Sie diese Richtlinien einhalten müssen.
Code mit Markierungen testen
Testszenario | Verwendeter Mechanismus |
---|---|
Lokale Tests, wenn sich die Flag-Werte häufig ändern | Android-Debug-Bridge wie unter Wert eines Flags zur Laufzeit ändern beschrieben |
Lokale Tests, wenn sich die Flag-Werte selten ändern | Datei mit Flag-Werten, wie unter Werte für das Flags der Funktionseinführung 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 |
End-to-End- oder Einheitentests, bei denen Flag-Werte nicht geändert werden können | CheckFlagsRule , wie unter End-to-End- oder Einheitentests 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 in die AndroidTest.xml
-Testmodulkonfigurationsdatei FeatureFlagTargetPreparer
- und Flag-Wertüberschreibungen 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 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üge
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. Dabei istname
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 Einheitentests 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. - Verwende 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 SetFlagsRule
eingebunden wird und mehrere Flags gleichzeitig aktiviert werden:
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, die zum Überschreiben von Flag-Werten bereitgestellt wird. Informationen dazu, wieSetFlagsRule
Standardwerte ermittelt, finden Sie unter Gerätestandardwerte.@EnableFlags
ist eine Annotation, die eine beliebige Anzahl von Flag-Namen akzeptiert. Verwenden Sie zum Deaktivieren von Flags@DisableFlags
. Sie können diese Annotationen entweder auf eine Methode oder auf eine Klasse anwenden.
Lege die Flag-Werte für den gesamten Testprozess fest, beginnend mit SetFlagsRule
, das vor jeglichen @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.
Achten Sie darauf, dass Flags richtig festgelegt sind
Wie bereits erwähnt, wird SetFlagsRule
mit der JUnit @Rule
-Annotation verwendet. Das bedeutet, dass SetFlagsRule
nicht sicherstellen kann, dass Ihre Flags während des Konstruktors der Testklasse oder von mit @BeforeClass
oder @AfterClass
annotierten Methoden korrekt festgelegt werden.
Verwenden Sie die SetFlagsRule.ClassRule
-Methode, damit Test-Fixierungen mit dem richtigen Klassenwert erstellt werden, damit sie erst mit einer @Before
-annotierten Einrichtungsmethode 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 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 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 die Parametrisierung auch verwenden, um Tests für mehrere 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() {...}
}
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. Zum Beispiel wird legacyBarLogic
nur ausgeführt, wenn FLAG_BAR
deaktiviert ist. Dies geschieht bei zwei der vier Flag-Kombinationen. Bei den anderen beiden Kombinationen wird legacyBarLogic
übersprungen.
Es gibt zwei Methoden, um die Parametrisierungen für Ihre Flags zu erstellen:
FlagsParameterization.allCombinationsOf(String...)
führt 2^n Durchläufe 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: Ein Flag führt 2x Tests und vier Flags 5x Flags aus.
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 der Makros
TEST
undTEST_F
werden die MakrosTEST_WITH_FLAGS
undTEST_F_WITH_FLAGS
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.REQUIRES_FLAGS_DISABLED
definiert eine Reihe von Feature-Flags, die die deaktivierte Bedingung 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 einer Konfigurationsdatei 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, das für Flags verwendet wird, die standardmäßig in der Gerätekonfiguration festgelegt sind.
- Anstelle der Makros
Fügen Sie in der Build-Datei
Android.bp
die von 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 dem folgenden Befehl lokal aus:
atest FlagMacrosTests
Wenn das Flag
my_namespace.android.myflag.tests.my_flag
deaktiviert 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_flag
aktiviert 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
Verwenden Sie für Testfälle, in denen Sie Flags nicht überschreiben und Tests nur filtern können, wenn sie auf dem aktuellen Flag-Status basieren, die Regel CheckFlagsRule
mit den Annotationen RequiresFlagsEnabled
und RequiresFlagsDisabled
.
Die folgenden Schritte zeigen, wie Sie einen End-to-End- oder Einheitentest 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-AnnotationenRequiresFlagsEnabled
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 dem Abschnitt
static_libs
der Build-Datei für Ihren Testjflag-unit
und eine von einer Konfiguration 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)
Standardwerte des Geräts
Die initialisierte SetFlagsRule
verwendet Flag-Werte vom Gerät. Wenn der Flag-Wert auf dem Gerät nicht überschrieben wird, z. B. bei ADB, entspricht der Standardwert der Releasekonfiguration des Builds. Wenn der Wert auf dem Gerät überschrieben wurde, verwendet SetFlagsRule
den Überschreibungswert als Standard.
Wenn derselbe Test unter verschiedenen Releasekonfigurationen ausgeführt wird, kann der Wert von Flags, die nicht explizit mit SetFlagsRule
festgelegt sind, 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.