С введением флагов запуска функций появились новые политики тестирования, которых вы должны придерживаться:
- Ваши тесты должны охватывать как включенное, так и выключенное поведение флага.
- Для установки значений флагов во время тестирования необходимо использовать официальные механизмы.
- Тесты 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— это аннотация, используемая для добавления зависимости флаг-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_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для фильтрации тестов. Также используйте аннотации 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, сгенерированные в раздел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 , чтобы не оказывать побочных эффектов на другие тестовые методы и классы.