隨著功能推出標記的推出,您必須遵守新的測試政策:
- 您的測試必須涵蓋標記的啟用和停用行為。
- 您必須使用官方機制,才能在測試期間設定標記值。
- xTS 測試不應在測試中覆寫旗標值。
下一節將說明您必須遵循這些政策的官方機制。
測試遭標示的程式碼
測試情境 | 使用的機制 |
---|---|
標記值經常變動時的本機測試 | 如「在執行階段變更標記的值」一文所述的 Android 偵錯橋接器 |
標記值不常變動時的本機測試 | 如「設定功能啟動旗標值」一節所述的旗標值檔案 |
標記值變更的端對端測試 | FeatureFlagTargetPreparer 如建立端對端測試一文所述 |
標記值變更的單元測試 | SetFlagsRule 搭配 @EnableFlags 和 @DisableFlags ,如「建立單元測試 (Java 和 Kotlin)」或「建立單元測試 (C 和 C++)」一文所述 |
端對端或單元測試 (標記值無法變更) | 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 標記值。
如要在含有大量標記的大型程式碼庫中編寫自動單元測試,請按照下列步驟操作:
- 使用
SetFlagsRule
類別搭配@EnableFlags
和@DisableFlags
註解,測試所有程式碼分支。 - 使用
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
是true
FLAG_FOO
是true
,FLAG_BAR
是false
FLAG_FOO
是false
,FLAG_BAR
是true
FLAG_FOO
為 false,FLAG_BAR
為false
@DisableFlags
和 @EnableFlags
註解不會直接變更旗標值,而是根據參數條件修改旗標值。舉例來說,legacyBarLogic
只會在 FLAG_BAR
停用時執行,而這會發生在四個標記組合中的兩個組合中。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)
是用於 aconfig 檔案中定義的旗標的巨集。這個巨集會接受命名空間 (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
,以免對其他測試方法和類別產生副作用。