С введением флагов запуска новых функций вам необходимо соблюдать новые правила тестирования:
- Ваши тесты должны охватывать как включенное, так и выключенное поведение флага.
- Для установки значений флагов во время тестирования необходимо использовать официальные механизмы.
- Тесты xTS не должны переопределять значения флагов в тестах.
В следующем разделе описаны официальные механизмы, которые вы должны использовать для соблюдения этих правил.
Проверьте свой помеченный код.
| Тестовый сценарий | Используемый механизм |
|---|---|
| Локальное тестирование при частом изменении значений флагов. | Отладочный мост Android, как обсуждалось в разделе «Изменение значения флага во время выполнения». |
| Локальное тестирование, когда значения флагов меняются нечасто. | Файл значений флагов, как обсуждалось в разделе «Установка значений флагов запуска функций». |
| Сквозное тестирование, в ходе которого изменяются значения флагов. | FeatureFlagTargetPreparer , как обсуждалось в разделе «Создание сквозных тестов». |
| Модульное тестирование при изменении значений флагов. | SetFlagsRule с аннотациями @EnableFlags и @DisableFlags , как описано в разделах «Создание модульных тестов (Java и Kotlin)» или «Создание модульных тестов (C и C++)». |
| Сквозное или модульное тестирование, при котором значения флагов не могут изменяться. | CheckFlagsRule , как обсуждалось в разделе «Создание сквозных или модульных тестов, в которых значения флагов не изменяются», |
Создавайте сквозные тесты.
AOSP предоставляет класс FeatureFlagTargetPreparer , который позволяет проводить сквозное тестирование на устройстве. Этот класс принимает в качестве входных данных переопределения значений флагов, устанавливает эти флаги в конфигурации устройства перед выполнением теста и восстанавливает флаги после выполнения.
Функциональность класса FeatureFlagTargetPreparer можно применять на уровне тестового модуля и конфигурации теста.
Примените FeatureFlagTargetPreparer в конфигурации тестового модуля.
Чтобы применить FeatureFlagTargetPreparer в конфигурации тестового модуля, добавьте FeatureFlagTargetPreparer и переопределения значений флагов в файл конфигурации тестового модуля 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>
Где:
-
target.preparer classвсегда имеет значениеcom.android.tradefed.targetprep.FeatureFlagTargetPreparer. -
optionпредставляет собой переопределение флага, при которомnameвсегда устанавливается равнымflag-value, аvalue—namespace/aconfigPackage.flagName=true|false.
Создавайте параметризованные тестовые модули на основе состояний флагов.
Для создания параметризованных тестовых модулей на основе состояний флагов:
Добавьте
FeatureFlagTargetPreparerв конфигурационный файл тестового модуляAndroidTest.xml:<target_preparer class="com.android.tradefed.targetprep.FeatureFlagTargetPreparer" >Укажите значения флагов в разделе
test_module_configфайла сборкиAndroid.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"}, ], }Поле
optionsсодержит параметры переопределения флагов, при этомnameвсегда устанавливается вflag-value, аvalueвnamespace/aconfigPackage.flagName=true|false.
Создайте модульные тесты (на Java и Kotlin).
В этом разделе описывается подход к переопределению значений флагов aconfig на уровне класса и метода (для каждого теста) в тестах Java и Kotlin.
Для написания автоматизированных модульных тестов в большом коде с большим количеством флагов выполните следующие шаги:
- Используйте класс
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используется для добавления зависимости `flags-JUnit` классаSetFlagsRule. -
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 приводит к тому, что test_flag_foo_turned_on завершается с ошибкой до выполнения, если значение FLAG_FLAG_FOO считывается конструктором класса DemoClass .
Если для всего класса необходимо включить флаг, переместите аннотацию @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 запусков каждого теста. Например, один флаг запускает 2x тестов, а четыре флага — 16x тестов.FlagsParameterization.progressionOf(String...)выполняет n+1 запусков каждого теста. Например, один флаг запускает 2 теста, а четыре флага — 5 тестов.
Создайте модульные тесты (на C и C++).
AOSP включает макросы для указания значений флагов в тестах на C и C++, написанных с использованием фреймворка GoogleTest.
В исходный код вашего теста включите определения макросов и библиотеки, сгенерированные aconfig:
#include <flag_macros.h> #include "android_cts_flags.h"В исходном коде ваших тестов вместо макросов
TESTиTESTFдля тестовых случаев используйтеTEST_WITH_FLAGSи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"); }Где:
- Вместо макросов
TESTиTEST_Fиспользуются макросыTEST_WITH_FLAGSиTEST_F_WITH_FLAGS. -
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для фильтрации тестов. Также используйте аннотации JavaRequiresFlagsEnabledи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-generated в раздел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 восстанавливает экземпляр FeatureFlags в Flags до его исходного значения FeatureFlagsImpl , чтобы избежать побочных эффектов для других методов и классов тестирования.