導入功能發布旗標後,您必須遵守新的測試政策:
- 測試必須涵蓋啟用和停用標記的行為。
- 測試期間,您必須使用官方機制設定旗標值。
- xTS 測試不應覆寫測試中的旗標值。
下一節將說明您必須使用的官方機制,確保遵守這些政策。
測試標記的程式碼
| 測試情境 | 使用的機制 |
|---|---|
| 旗標值經常變更時的本機測試 | 如「在執行階段變更旗標的值」一節所述的 Android 偵錯橋接器 |
| 旗標值不常變動時的本機測試 | 如「設定功能發布旗標值」一節所述的旗標值檔案 |
| 旗標值會變更的端對端測試 | FeatureFlagTargetPreparer,如「建立端對端測試」一文所述 |
| 單元測試:旗標值變更 | SetFlagsRule,如「建立單元測試 (Java 和 Kotlin)」或「建立單元測試 (C 和 C++)」所述@EnableFlags@DisableFlags |
| 無法變更旗標值的端對端或單元測試 | CheckFlagsRule,如「建立標記值不會變更的端對端或單元測試」一節所述 |
建立端對端測試
AOSP 提供名為 FeatureFlagTargetPreparer 的類別,可在裝置上進行端對端測試。這個類別會接受旗標值覆寫做為輸入內容,在測試執行前於裝置設定中設定這些旗標,並在執行後還原旗標。
您可以在測試模組和測試設定層級套用 FeatureFlagTargetPreparer 類別的功能。
在測試模組設定中套用 FeatureFlagTargetPreparer
如要在測試模組設定中套用 FeatureFlagTargetPreparer,請在 AndroidTest.xml 測試模組設定檔中加入 FeatureFlagTargetPreparer 和旗標值覆寫:
<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>
其中:
target.preparer class一律設為com.android.tradefed.targetprep.FeatureFlagTargetPreparer。option是旗標覆寫,其中name一律設為flag-value,而value則設為namespace/aconfigPackage.flagName=true|false。
根據旗標狀態建立參數化測試模組
如要根據旗標狀態建立參數化測試模組,請按照下列步驟操作:
在
AndroidTest.xml測試模組設定檔中加入FeatureFlagTargetPreparer:<target_preparer class="com.android.tradefed.targetprep.FeatureFlagTargetPreparer" >在
Android.bp建構檔案的test_module_config區段中,指定旗標值選項: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"}, ], }options欄位包含旗標覆寫,且name一律設為flag-value,value則設為namespace/aconfigPackage.flagName=true|false。
建立單元測試 (Java 和 Kotlin)
本節說明如何在 Java 和 Kotlin 測試中,於類別和方法層級 (每個測試) 覆寫 aconfig 旗標值。
如要在含有大量標記的大型程式碼集編寫自動單元測試,請按照下列步驟操作:
- 搭配
@EnableFlags和@DisableFlags註解使用SetFlagsRule類別,即可測試所有程式碼分支。 - 使用
SetFlagsRule.ClassRule方法,避免常見的測試錯誤。 - 使用
FlagsParameterization測試各種旗標設定的類別。
測試所有程式碼分支
對於使用靜態類別存取旗標的專案,系統會提供 SetFlagsRule 輔助類別來覆寫旗標值。下列程式碼片段說明如何納入 SetFlagsRule 並一次啟用多個標記:
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() {
...
}
其中:
@Rule註解用於新增SetFlagsRule類別的旗標 JUnit 依附元件。SetFlagsRule是用於覆寫旗標值的輔助類別。如要瞭解SetFlagsRule如何判斷預設值,請參閱「裝置預設值」。@EnableFlags是一種註解,可接受任意數量的旗標名稱。如要停用旗標,請使用@DisableFlags。您可以將這些註解套用至方法或類別。
為整個測試程序設定旗標值,從 SetFlagsRule 開始,這會優先於測試中的任何 @Before 註解設定方法。SetFlagsRule 完成後 (即任何 @After 註解設定方法之後),旗標值會恢復先前的狀態。
確認旗標設定正確無誤
如先前所述,SetFlagsRule 會與 JUnit @Rule 註解搭配使用,因此 SetFlagsRule 無法確保在測試類別的建構函式或任何 @BeforeClass 或 @AfterClass 註解方法中,旗標設定正確無誤。
為確保測試固定裝置是以正確的類別值建構,請使用 SetFlagsRule.ClassRule 方法,這樣固定裝置就不會建立,直到有 @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() {
...
}
}
新增 SetFlagsRule.ClassRule 類別規則後,建構函式讀取 DemoClass 的 FLAG_FLAG_FOO 時,test_flag_foo_turned_on 會在執行前失敗。
如果整個類別都需要啟用標記,請將 @EnableFlags 註解移至類別層級 (類別宣告之前)。將註解移至類別層級,可讓 SetFlagsRule.ClassRule 確保在測試類別的建構函式中,或在任何 @BeforeClass 或 @AfterClass 註解方法中,正確設定旗標。
在多個旗標設定中執行測試
由於您可以根據個別測試設定旗標值,因此也可以使用參數化,跨多個旗標設定執行測試:
...
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() {...}
}
請注意,使用 SetFlagsRule 但未參數化時,這個類別會執行三項測試 (fooLogic、legacyBarLogic 和 newBarLogic)。fooLogic 方法會使用裝置上設定的 FLAG_FOO 和 FLAG_BAR 值執行。
加入參數化後,FlagsParameterization.allCombinationsOf 方法會建立 FLAG_FOO 和 FLAG_BAR 旗標的所有可能組合:
FLAG_FOO為true,且FLAG_BAR為trueFLAG_FOO為true,且FLAG_BAR為falseFLAG_FOO為false,且FLAG_BAR為trueFLAG_FOO為 false,且FLAG_BAR為false
@DisableFlags 和 @EnableFlags 註解會根據參數條件修改旗標值,而不是直接變更旗標值。舉例來說,只有在停用 FLAG_BAR 時,legacyBarLogic 才會執行,而這只會在四種標記組合中的兩種發生。其他兩種組合會略過 legacyBarLogic。
建立旗標參數化的方法有兩種:
FlagsParameterization.allCombinationsOf(String...)會執行每個測試 2^n 次。舉例來說,一個旗標會執行 2 次測試,四個旗標則會執行 16 次測試。FlagsParameterization.progressionOf(String...)會執行每個測試 n+1 次。舉例來說,一個旗標會執行 2 倍測試,四個旗標則會執行 5 倍測試。
建立單元測試 (C 和 C++)
AOSP 包含以 GoogleTest 架構編寫的 C 和 C++ 測試適用的旗標值巨集。
在測試來源中,加入巨集定義和 aconfig 產生的程式庫:
#include <flag_macros.h> #include "android_cts_flags.h"在測試來源中,請使用
TEST_WITH_FLAGS和TEST_F_WITH_FLAGS,而非TEST和TESTF巨集:#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"); }其中:
- 改用
TEST_WITH_FLAGS和TEST_F_WITH_FLAGS巨集,而非TEST和TEST_F巨集。 REQUIRES_FLAGS_ENABLED定義一組必須符合啟用條件的功能發布標記。您可以在ACONFIG_FLAG或LEGACY_FLAG巨集中寫入這些旗標。REQUIRES_FLAGS_DISABLED定義一組必須符合停用條件的特徵旗標。您可以在ACONFIG_FLAG或LEGACY_FLAG巨集中寫入這些旗標。ACONFIG_FLAG (TEST_NS, readwrite_enabled_flag)是用於設定檔中定義的旗標巨集。這個巨集會接受命名空間 (TEST_NS) 和旗標名稱 (readwrite_enabled_flag)。LEGACY_FLAG(aconfig_flags.cts, TEST_NS, readwrite_disabled_flag)是用於預設在裝置設定中設定的巨集。
- 改用
在
Android.bp建構檔案中,將 aconfig 產生的程式庫和相關巨集程式庫新增為測試依附元件: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"], ... }使用下列指令在本機執行測試:
atest FlagMacrosTests如果停用
my_namespace.android.myflag.tests.my_flag旗標,測試結果如下:[1/2] MyTest#test1: IGNORED (0ms) [2/2] MyTestF#test2: PASSED (0ms)如果啟用
my_namespace.android.myflag.tests.my_flag旗標,測試結果如下:[1/2] MyTest#test1: PASSED (0ms) [2/2] MyTestF#test2: IGNORED (0ms)
建立不會變更旗標值的端對端或單元測試
如果無法覆寫標記,且只能根據目前的標記狀態篩選測試,請使用 CheckFlagsRule 規則搭配 RequiresFlagsEnabled 和 RequiresFlagsDisabled 註解。
下列步驟說明如何建立及執行無法覆寫旗標值的端對端或單元測試:
在測試程式碼中,使用
CheckFlagsRule套用測試篩選條件。此外,請使用 Java 註解RequiresFlagsEnabled和RequiredFlagsDisabled,指定測試的旗標需求。裝置端測試會使用
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() {} }主機端測試會使用
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() {} }將
jflag-unit和 aconfig 產生的程式庫新增至測試的建構檔案static_libs區段:android_test { name: "FlagAnnotationTests", srcs: ["*.java"], static_libs: [ "androidx.test.rules", "my_aconfig_lib", "flag-junit", "platform-test-annotations", ], test_suites: ["general-tests"], }使用下列指令在本機執行測試:
atest FlagAnnotationTests如果停用
Flags.FLAG_FLAG_NAME_1旗標,測試結果如下:[1/2] com.cts.flags.FlagAnnotationTest#test1: ASSUMPTION_FAILED (10ms) [2/2] com.cts.flags.FlagAnnotationTest#test2: PASSED (2ms)否則測試結果為:
[1/2] com.cts.flags.FlagAnnotationTest#test1: PASSED (2ms) [2/2] com.cts.flags.FlagAnnotationTest#test2: ASSUMPTION_FAILED (10ms)
裝置預設值
初始化的 SetFlagsRule 會使用裝置中的旗標值。如果裝置上的旗標值未遭到覆寫 (例如使用 adb),預設值會與建構版本的發布設定相同。如果裝置上的值已遭覆寫,SetFlagsRule 會將覆寫值做為預設值。
如果以不同的發布設定執行相同測試,則未以 SetFlagsRule 明確設定的旗標值可能會有所不同。
每次測試後,SetFlagsRule 會將 Flags 中的 FeatureFlags 例項還原為原始 FeatureFlagsImpl,因此不會對其他測試方法和類別產生副作用。