기능 출시 플래그가 도입되면서 준수해야 하는 새로운 테스트 정책이 생겼습니다.
- 테스트는 플래그의 사용 설정된 동작과 사용 중지된 동작을 모두 포함해야 합니다.
- 테스트 중에 플래그 값을 설정하려면 공식 메커니즘을 사용해야 합니다.
- 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 플래그 값을 재정의하는 방법을 설명합니다.
플래그 수가 많은 대규모 코드베이스에서 자동화된 단위 테스트를 작성하려면 다음 단계를 따르세요.
@EnableFlags및@DisableFlags주석이 포함된SetFlagsRule클래스를 사용하여 모든 코드 브랜치를 테스트합니다.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를 사용합니다. 이러한 주석은 메서드 또는 클래스에 적용할 수 있습니다.
테스트의 @Before 주석이 달린 설정 메서드보다 먼저인 SetFlagsRule부터 시작하여 전체 테스트 프로세스의 플래그 값을 설정합니다. 플래그 값은 @After 주석이 달린 설정 메서드 이후인 SetFlagsRule이 완료되면 이전 상태로 돌아갑니다.
플래그가 올바르게 설정되었는지 확인
앞서 언급했듯이 SetFlagsRule은 JUnit @Rule 주석과 함께 사용됩니다. 즉, SetFlagsRule은 테스트 클래스의 생성자 또는 @BeforeClass 또는 @AfterClass 주석이 달린 메서드 중에 플래그가 올바르게 설정되었는지 확인할 수 없습니다.
테스트 픽스처가 올바른 클래스 값으로 생성되도록 하려면 @Before 주석이 달린 설정 메서드가 실행될 때까지 픽스처가 생성되지 않도록 SetFlagsRule.ClassRule 메서드를 사용하세요.
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 클래스 규칙을 추가하면 FLAG_FLAG_FOO가 DemoClass의 생성자에 의해 읽힐 때 실행 전에 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가 사용 중지된 경우에만 실행되며 이는 4개의 플래그 조합 중 2개에서 발생합니다. 다른 두 조합의 경우 legacyBarLogic이 건너뜁니다.
플래그의 매개변수화를 만드는 방법에는 두 가지가 있습니다.
FlagsParameterization.allCombinationsOf(String...)는 각 테스트의 2^n 실행을 실행합니다. 예를 들어 플래그 하나는 2x 테스트를 실행하고 플래그 4개는 16x 테스트를 실행합니다.FlagsParameterization.progressionOf(String...)는 각 테스트의 n+1 실행을 실행합니다. 예를 들어 플래그 하나는 2x 테스트를 실행하고 플래그 4개는 5x 플래그를 실행합니다.
단위 테스트 (C 및 C++) 만들기
AOSP에는 GoogleTest 프레임워크로 작성된 C 및 C++ 테스트의 플래그 값 매크로가 포함되어 있습니다.
- 에서 찾을 수 있습니다.
테스트 소스에서 매크로 정의 및 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)는 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 FlagMacrosTestsmy_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)
플래그 값이 변경되지 않는 엔드 투 엔드 또는 단위 테스트 만들기
플래그를 재정의할 수 없고 현재 플래그 상태를 기반으로 하는 경우에만 테스트를 필터링할 수 있는 테스트 사례의 경우 RequiresFlagsEnabled 및 RequiresFlagsDisabled 주석이 포함된 CheckFlagsRule 규칙을 사용합니다.
다음 단계에서는 플래그 값을 재정의할 수 없는 엔드 투 엔드 또는 단위 테스트를 만들고 실행하는 방법을 보여줍니다.
테스트 코드에서
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() {} }테스트의 빌드 파일의
static_libs섹션에jflag-unit및 aconfig 생성 라이브러리를 추가합니다.android_test { name: "FlagAnnotationTests", srcs: ["*.java"], static_libs: [ "androidx.test.rules", "my_aconfig_lib", "flag-junit", "platform-test-annotations", ], test_suites: ["general-tests"], }다음 명령어를 사용하여 로컬에서 테스트를 실행합니다.
atest FlagAnnotationTestsFlags.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로 복원합니다.