С введением флагов запуска функций появились новые политики тестирования, которых вы должны придерживаться:
- Ваши тесты должны охватывать как включенное, так и отключенное поведение флага.
- Вы должны использовать официальные механизмы для установки значений флагов во время тестирования.
- Тесты 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
— это аннотация, используемая для добавления зависимости flag-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 прогонов каждого теста. Например, один флаг запускает 2 теста, а четыре флага — 16 тестов.FlagsParameterization.progressionOf(String...)
выполняет n+1 прогон каждого теста. Например, один флаг запускает 2x тестов, а четыре флага запускают 5x флагов.
Создание модульных тестов (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)
— это макрос, используемый для флагов, определенных в файлах конфигурации. Этот макрос принимает пространство имен (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
, чтобы он не оказывал побочных эффектов на другие методы и классы тестирования.