Khi giới thiệu cờ ra mắt tính năng, bạn phải tuân thủ các chính sách kiểm thử mới:
- Các quy trình kiểm thử của bạn phải bao gồm cả hành vi bật và tắt của cờ.
- Bạn phải sử dụng các cơ chế chính thức để đặt giá trị cờ trong quá trình kiểm thử.
- Các kiểm thử xTS không được ghi đè các giá trị cờ trong kiểm thử.
Phần tiếp theo trình bày các cơ chế chính thức mà bạn phải sử dụng để tuân thủ các chính sách này.
Kiểm thử mã bị gắn cờ
| Tình huống kiểm thử | Cơ chế được dùng |
|---|---|
| Kiểm thử cục bộ khi giá trị cờ thường xuyên thay đổi | Cầu gỡ lỗi Android như đã thảo luận trong phần Thay đổi giá trị của cờ trong thời gian chạy |
| Kiểm thử cục bộ khi giá trị cờ không thường xuyên thay đổi | Tệp giá trị cờ như đã thảo luận trong phần Đặt giá trị cờ ra mắt tính năng |
| Kiểm thử toàn diện trong đó các giá trị cờ thay đổi | FeatureFlagTargetPreparer như đã thảo luận trong phần Tạo kiểm thử toàn diện |
| Kiểm thử đơn vị khi các giá trị cờ thay đổi | SetFlagsRule với @EnableFlags và @DisableFlags như đã thảo luận trong phần Tạo kiểm thử đơn vị (Java và Kotlin) hoặc Tạo kiểm thử đơn vị (C và C++) |
| Kiểm thử đơn vị hoặc kiểm thử toàn diện mà giá trị cờ không thể thay đổi | CheckFlagsRule như đã thảo luận trong phần Tạo kiểm thử đơn vị hoặc kiểm thử toàn diện khi giá trị cờ không thay đổi |
Tạo các bài kiểm thử toàn diện
AOSP cung cấp một lớp có tên là FeatureFlagTargetPreparer, cho phép kiểm thử toàn diện trên một thiết bị. Lớp này chấp nhận các giá trị ghi đè cờ làm dữ liệu đầu vào, đặt các cờ đó trong cấu hình thiết bị trước khi thực thi kiểm thử và khôi phục cờ sau khi thực thi.
Bạn có thể áp dụng chức năng của lớp FeatureFlagTargetPreparer ở cấp độ mô-đun kiểm thử và cấu hình kiểm thử.
Áp dụng FeatureFlagTargetPreparer trong cấu hình mô-đun kiểm thử
Để áp dụng FeatureFlagTargetPreparer trong cấu hình mô-đun kiểm thử, hãy thêm FeatureFlagTargetPreparer và các giá trị ghi đè cờ vào tệp cấu hình mô-đun kiểm thử AndroidTest.xml:
<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>
Trong trường hợp:
target.preparer classluôn được đặt thànhcom.android.tradefed.targetprep.FeatureFlagTargetPreparer.optionlà chế độ ghi đè cờ vớinameluôn được đặt thànhflag-valuevàvalueđược đặt thànhnamespace/aconfigPackage.flagName=true|false.
Tạo các mô-đun kiểm thử được tham số hoá dựa trên trạng thái cờ
Cách tạo mô-đun kiểm thử được tham số hoá dựa trên trạng thái cờ:
Đưa
FeatureFlagTargetPreparervào tệp cấu hình mô-đun kiểm thửAndroidTest.xml:<target_preparer class="com.android.tradefed.targetprep.FeatureFlagTargetPreparer" >Chỉ định các lựa chọn về giá trị cờ trong phần
test_module_configcủa tệp bản dựngAndroid.bp: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"}, ], }Trường
optionschứa các chế độ ghi đè cờ vớinameluôn được đặt thànhflag-valuevàvalueđược đặt thànhnamespace/aconfigPackage.flagName=true|false.
Tạo kiểm thử đơn vị (Java và Kotlin)
Phần này mô tả phương pháp ghi đè các giá trị cờ aconfig ở cấp lớp và phương thức (mỗi kiểm thử) trong các kiểm thử Java và Kotlin.
Để viết các bài kiểm thử đơn vị tự động trong một cơ sở mã lớn với nhiều cờ, hãy làm theo các bước sau:
- Sử dụng lớp
SetFlagsRulevới các chú giải@EnableFlagsvà@DisableFlagsđể kiểm thử tất cả các nhánh mã. - Sử dụng phương thức
SetFlagsRule.ClassRuleđể tránh các lỗi kiểm thử thường gặp. - Sử dụng
FlagsParameterizationđể kiểm thử các lớp trên nhiều cấu hình cờ.
Kiểm thử tất cả các nhánh mã
Đối với những dự án sử dụng lớp tĩnh để truy cập vào các cờ, lớp trợ giúp SetFlagsRule được cung cấp để ghi đè các giá trị cờ. Đoạn mã sau đây cho biết cách thêm SetFlagsRule và bật nhiều cờ cùng một lúc:
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() {
...
}
Trong trường hợp:
@Rulelà một chú giải dùng để thêm phần phụ thuộc flag-JUnit của lớpSetFlagsRule.SetFlagsRulelà lớp hỗ trợ được cung cấp để ghi đè các giá trị cờ. Để biết thông tin về cáchSetFlagsRulexác định giá trị mặc định, hãy xem Giá trị mặc định của thiết bị.@EnableFlagslà một chú thích chấp nhận một số lượng tuỳ ý tên cờ. Khi tắt cờ, hãy sử dụng@DisableFlags. Bạn có thể áp dụng các chú thích này cho một phương thức hoặc một lớp.
Đặt giá trị cờ cho toàn bộ quy trình kiểm thử, bắt đầu bằng SetFlagsRule, trước bất kỳ phương thức thiết lập nào được chú thích bằng @Before trong quy trình kiểm thử. Các giá trị cờ sẽ trở về trạng thái trước đó khi SetFlagsRule kết thúc, tức là sau mọi phương thức thiết lập được chú thích bằng @After.
Đảm bảo bạn đặt cờ chính xác
Như đã đề cập trước đó, SetFlagsRule được dùng với chú thích @Rule của JUnit, tức là SetFlagsRule không thể đảm bảo các cờ của bạn được đặt đúng cách trong hàm khởi tạo của lớp kiểm thử hoặc bất kỳ phương thức nào được chú thích bằng @BeforeClass hoặc @AfterClass.
Để đảm bảo rằng các đối tượng kiểm thử được tạo bằng giá trị lớp chính xác, hãy sử dụng phương thức SetFlagsRule.ClassRule để các đối tượng của bạn không được tạo cho đến khi có một phương thức thiết lập được chú thích @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() {
...
}
}
Bằng cách thêm quy tắc lớp SetFlagsRule.ClassRule, test_flag_foo_turned_on sẽ không thành công trước khi chạy khi FLAG_FLAG_FOO được hàm khởi tạo của DemoClass đọc.
Nếu toàn bộ lớp của bạn cần bật một cờ, hãy di chuyển chú thích @EnableFlags đến cấp lớp (trước khai báo lớp). Việc di chuyển chú giải đến cấp lớp cho phép SetFlagsRule.ClassRule đảm bảo cờ được đặt chính xác trong hàm khởi tạo của lớp kiểm thử hoặc trong mọi phương thức được chú giải @BeforeClass hoặc @AfterClass.
Chạy kiểm thử trên nhiều cấu hình cờ
Vì bạn có thể đặt giá trị cờ trên cơ sở mỗi lượt kiểm thử, nên bạn cũng có thể sử dụng tham số hoá để chạy kiểm thử trên nhiều cấu hình cờ:
...
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() {...}
}
Lưu ý rằng với SetFlagsRule nhưng không có tham số hoá, lớp này sẽ chạy 3 kiểm thử (fooLogic, legacyBarLogic và newBarLogic). Phương thức fooLogic chạy với bất kỳ giá trị nào của FLAG_FOO và FLAG_BAR được đặt trên thiết bị.
Khi bạn thêm tham số hoá, phương thức FlagsParameterization.allCombinationsOf sẽ tạo tất cả các tổ hợp có thể có của cờ FLAG_FOO và FLAG_BAR:
FLAG_FOOlàtruevàFLAG_BARlàtrueFLAG_FOOlàtruevàFLAG_BARlàfalseFLAG_FOOlàfalsevàFLAG_BARlàtrueFLAG_FOOlà false vàFLAG_BARlàfalse
Thay vì trực tiếp thay đổi các giá trị cờ, chú thích @DisableFlags và @EnableFlags sẽ sửa đổi các giá trị cờ dựa trên các điều kiện tham số. Ví dụ: legacyBarLogic chỉ chạy khi FLAG_BAR bị vô hiệu hoá, điều này xảy ra trong 2 trong số 4 tổ hợp cờ. legacyBarLogic sẽ bị bỏ qua đối với 2 tổ hợp còn lại.
Có hai phương thức để tạo tham số hoá cho cờ của bạn:
FlagsParameterization.allCombinationsOf(String...)thực thi 2^n lần chạy của mỗi bài kiểm thử. Ví dụ: một cờ chạy 2x thử nghiệm hoặc 4 cờ chạy 16x thử nghiệm.FlagsParameterization.progressionOf(String...)thực thi n+1 lần chạy của mỗi bài kiểm thử. Ví dụ: một cờ chạy 2 bài kiểm thử và 4 cờ chạy 5 cờ.
Tạo kiểm thử đơn vị (C và C++)
AOSP bao gồm các macro giá trị cờ cho các kiểm thử C và C++ được viết trong khung GoogleTest.
Trong nguồn kiểm thử, hãy thêm các định nghĩa macro và thư viện do aconfig tạo:
#include <flag_macros.h> #include "android_cts_flags.h"Trong nguồn kiểm thử, thay vì sử dụng macro
TESTvàTESTFcho các trường hợp kiểm thử, hãy sử dụngTEST_WITH_FLAGSvàTEST_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"); }Trong trường hợp:
- Macro
TEST_WITH_FLAGSvàTEST_F_WITH_FLAGSđược dùng thay cho macroTESTvàTEST_F. REQUIRES_FLAGS_ENABLEDxác định một tập hợp các cờ phát hành tính năng phải đáp ứng điều kiện đã bật. Bạn có thể viết các cờ này trong macroACONFIG_FLAGhoặcLEGACY_FLAG.REQUIRES_FLAGS_DISABLEDxác định một nhóm cờ tính năng phải đáp ứng điều kiện bị vô hiệu hoá. Bạn có thể viết các cờ này trong macroACONFIG_FLAGhoặcLEGACY_FLAG.ACONFIG_FLAG (TEST_NS, readwrite_enabled_flag)là một macro dùng cho các cờ được xác định trong tệp aconfig. Macro này chấp nhận một không gian tên (TEST_NS) và một tên cờ (readwrite_enabled_flag).LEGACY_FLAG(aconfig_flags.cts, TEST_NS, readwrite_disabled_flag)là một macro được dùng cho các cờ được đặt trong cấu hình thiết bị theo mặc định.
- Macro
Trong tệp bản dựng
Android.bp, hãy thêm các thư viện do aconfig tạo và các thư viện macro có liên quan làm phần phụ thuộc kiểm thử: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"], ... }Chạy các kiểm thử cục bộ bằng lệnh sau:
atest FlagMacrosTestsNếu cờ
my_namespace.android.myflag.tests.my_flagbị tắt, kết quả kiểm thử sẽ là:[1/2] MyTest#test1: IGNORED (0ms) [2/2] MyTestF#test2: PASSED (0ms)Nếu cờ
my_namespace.android.myflag.tests.my_flagđược bật, kết quả kiểm thử sẽ là:[1/2] MyTest#test1: PASSED (0ms) [2/2] MyTestF#test2: IGNORED (0ms)
Tạo các kiểm thử đơn vị hoặc kiểm thử toàn diện trong đó các giá trị cờ không thay đổi
Đối với những trường hợp kiểm thử mà bạn không thể ghi đè cờ và chỉ có thể lọc các kiểm thử nếu chúng dựa trên trạng thái cờ hiện tại, hãy sử dụng quy tắc CheckFlagsRule với chú thích RequiresFlagsEnabled và RequiresFlagsDisabled.
Các bước sau đây hướng dẫn bạn cách tạo và chạy một kiểm thử đơn vị hoặc kiểm thử từ đầu đến cuối mà trong đó bạn không thể ghi đè các giá trị cờ:
Trong mã kiểm thử, hãy sử dụng
CheckFlagsRuleđể áp dụng tính năng lọc kiểm thử. Ngoài ra, hãy sử dụng chú thích JavaRequiresFlagsEnabledvàRequiredFlagsDisabledđể chỉ định các yêu cầu về cờ cho kiểm thử của bạn.Thử nghiệm phía thiết bị sử dụng lớp
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() {} }Kiểm thử phía máy chủ lưu trữ sử dụng lớp
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() {} }Thêm
jflag-unitvà các thư viện do aconfig tạo vào phầnstatic_libscủa tệp bản dựng cho kiểm thử:android_test { name: "FlagAnnotationTests", srcs: ["*.java"], static_libs: [ "androidx.test.rules", "my_aconfig_lib", "flag-junit", "platform-test-annotations", ], test_suites: ["general-tests"], }Sử dụng lệnh sau để chạy thử nghiệm cục bộ:
atest FlagAnnotationTestsNếu cờ
Flags.FLAG_FLAG_NAME_1bị tắt, kết quả kiểm thử sẽ là:[1/2] com.cts.flags.FlagAnnotationTest#test1: ASSUMPTION_FAILED (10ms) [2/2] com.cts.flags.FlagAnnotationTest#test2: PASSED (2ms)Nếu không, kết quả kiểm tra sẽ là:
[1/2] com.cts.flags.FlagAnnotationTest#test1: PASSED (2ms) [2/2] com.cts.flags.FlagAnnotationTest#test2: ASSUMPTION_FAILED (10ms)
Giá trị mặc định của thiết bị
SetFlagsRule đã khởi chạy sẽ sử dụng các giá trị cờ từ thiết bị. Nếu giá trị cờ trên thiết bị không bị ghi đè (chẳng hạn như bằng adb), thì giá trị mặc định sẽ giống như cấu hình phát hành của bản dựng. Nếu giá trị trên thiết bị đã bị ghi đè, thì SetFlagsRule sẽ sử dụng giá trị ghi đè làm giá trị mặc định.
Nếu cùng một kiểm thử được thực thi theo nhiều cấu hình phát hành, thì giá trị của các cờ không được đặt rõ ràng bằng SetFlagsRule có thể khác nhau.
Sau mỗi bài kiểm thử, SetFlagsRule sẽ khôi phục thực thể FeatureFlags trong Flags về FeatureFlagsImpl ban đầu để không ảnh hưởng đến các phương thức và lớp kiểm thử khác.