کد را در پرچم های راه اندازی ویژگی تست کنید

با معرفی پرچم‌های راه‌اندازی ویژگی، سیاست‌های تست جدیدی وجود دارد که باید به آنها پایبند باشید:

  • آزمایش‌های شما باید هر دو رفتار فعال و غیرفعال پرچم را پوشش دهند.
  • شما باید از سازوکارهای رسمی برای تنظیم مقادیر پرچم در طول آزمایش استفاده کنید.
  • تست‌های xTS نباید مقادیر پرچم را در تست‌ها لغو کنند.

بخش بعدی سازوکارهای رسمی مورد نیاز برای پایبندی به این سیاست‌ها را ارائه می‌دهد.

کد علامت‌گذاری شده خود را آزمایش کنید

سناریوی آزمایش مکانیسم مورد استفاده
تست محلی زمانی که مقادیر پرچم اغلب تغییر می‌کنند پل اشکال‌زدایی اندروید، همانطور که در تغییر مقدار یک پرچم در زمان اجرا بحث شد
تست محلی زمانی که مقادیر پرچم اغلب تغییر نمی‌کنند فایل مقادیر پرچم همانطور که در بخش «تنظیم مقادیر پرچم راه‌اندازی ویژگی» بحث شد، ذخیره می‌شود.
تست سرتاسری که در آن مقادیر پرچم تغییر می‌کنند FeatureFlagTargetPreparer همانطور که در بخش ایجاد تست‌های سرتاسری بحث شد
تست واحد که در آن مقادیر پرچم تغییر می‌کنند همانطور که در بخش ایجاد تست‌های واحد (جاوا و کاتلین) یا ایجاد تست‌های واحد (سی و سی پلاس پلاس) بحث شد، SetFlagsRule با @EnableFlags و @DisableFlags تنظیم کنید.
تست سرتاسری یا واحد که در آن مقادیر پرچم نمی‌توانند تغییر کنند CheckFlagsRule همانطور که در بخش ایجاد تست‌های سرتاسری یا واحد که مقادیر پرچم تغییر نمی‌کنند، بحث شده است.

ایجاد تست‌های سرتاسری

AOSP کلاسی به نام FeatureFlagTargetPreparer ارائه می‌دهد که امکان تست سرتاسری (end-to-end testing) را روی یک دستگاه فراهم می‌کند. این کلاس مقادیر flag را به عنوان ورودی می‌پذیرد، قبل از اجرای تست، آن flagها را در پیکربندی دستگاه تنظیم می‌کند و پس از اجرا، flagها را بازیابی می‌کند.

شما می‌توانید عملکرد کلاس FeatureFlagTargetPreparer را در ماژول تست و سطوح پیکربندی تست اعمال کنید.

اعمال FeatureFlagTargetPreparer در پیکربندی ماژول آزمایشی

برای اعمال FeatureFlagTargetPreparer در پیکربندی ماژول تست، FeatureFlagTargetPreparer و مقادیر flag را در فایل پیکربندی ماژول تست 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 تنظیم می‌شود.

ایجاد ماژول‌های تست پارامتری بر اساس وضعیت پرچم‌ها

برای ایجاد ماژول‌های تست پارامتری بر اساس وضعیت پرچم‌ها:

  1. FeatureFlagTargetPreparer در فایل پیکربندی ماژول تست AndroidTest.xml قرار دهید:

    <target_preparer class="com.android.tradefed.targetprep.FeatureFlagTargetPreparer" >
    
  2. گزینه‌های مقدار پرچم را در بخش 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 شامل لغو flag است که name همیشه روی flag-value و value روی namespace/aconfigPackage.flagName=true|false تنظیم شده است.

ایجاد تست‌های واحد (جاوا و کاتلین)

این بخش رویکرد لغو مقادیر پرچم aconfig در سطح کلاس و متد (به ازای هر تست) در تست‌های جاوا و کاتلین را شرح می‌دهد.

برای نوشتن تست‌های واحد خودکار در یک پایگاه کد بزرگ با تعداد زیادی پرچم، این مراحل را دنبال کنید:

  1. از کلاس SetFlagsRule به همراه حاشیه‌نویسی‌های @EnableFlags و @DisableFlags برای آزمایش تمام شاخه‌های کد استفاده کنید.
  2. برای جلوگیری از باگ‌های رایج در تست، از متد SetFlagsRule.ClassRule استفاده کنید.
  3. 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 یک کلاس کمکی است که برای لغو مقادیر flag ارائه شده است. برای اطلاعات بیشتر در مورد نحوه تعیین مقادیر پیش‌فرض SetFlagsRule ، به مقادیر پیش‌فرض دستگاه مراجعه کنید.
  • @EnableFlags یک حاشیه‌نویسی است که تعداد دلخواهی از نام‌های پرچم را می‌پذیرد. هنگام غیرفعال کردن پرچم‌ها، از @DisableFlags استفاده کنید. می‌توانید این حاشیه‌نویسی‌ها را روی یک متد یا یک کلاس اعمال کنید.

مقادیر پرچم را برای کل فرآیند تست تنظیم کنید، که با SetFlagsRule شروع می‌شود، که قبل از هر متد راه‌اندازی @Before -annotated در تست قرار دارد. مقادیر پرچم پس از اتمام SetFlagsRule به حالت قبلی خود برمی‌گردند، که بعد از هر متد راه‌اندازی @After -annotated است.

مطمئن شوید که پرچم‌ها (flags) به درستی تنظیم شده‌اند

همانطور که قبلاً ذکر شد، SetFlagsRule با حاشیه‌نویسی JUnit @Rule استفاده می‌شود، به این معنی که SetFlagsRule نمی‌تواند اطمینان حاصل کند که پرچم‌های شما در طول سازنده کلاس تست یا هر متد حاشیه‌نویسی شده @BeforeClass یا @AfterClass به درستی تنظیم شده‌اند.

برای اطمینان از اینکه fixture های آزمایشی با مقدار کلاس صحیح ساخته می‌شوند، از متد SetFlagsRule.ClassRule استفاده کنید تا fixture های شما تا زمانی که یک متد setup با حاشیه‌نویسی @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 -annotated به درستی تنظیم شده است.

اجرای تست‌ها در پیکربندی‌های مختلف پرچم

از آنجا که می‌توانید مقادیر پرچم را بر اساس هر آزمون تنظیم کنید، می‌توانید از پارامتربندی برای اجرای آزمون‌ها در چندین پیکربندی پرچم نیز استفاده کنید:

...
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 نادرست و FLAG_BAR false است.

به جای تغییر مستقیم مقادیر پرچم، حاشیه‌نویسی‌های @DisableFlags و @EnableFlags مقادیر پرچم را بر اساس شرایط پارامتر تغییر می‌دهند. برای مثال، legacyBarLogic فقط زمانی اجرا می‌شود که FLAG_BAR غیرفعال باشد، که در دو مورد از چهار ترکیب پرچم رخ می‌دهد. legacyBarLogic برای دو ترکیب دیگر نادیده گرفته می‌شود.

دو روش برای ایجاد پارامتربندی برای پرچم‌های شما وجود دارد:

  • FlagsParameterization.allCombinationsOf(String...) 2^n بار از هر تست را اجرا می‌کند. برای مثال، یک فلگ 2x تست یا چهار فلگ 16x تست را اجرا می‌کنند.

  • FlagsParameterization.progressionOf(String...) تعداد n+1 اجرا از هر تست را اجرا می‌کند. برای مثال، یک فلگ، 2x تست و چهار فلگ، 5x فلگ را اجرا می‌کنند.

ایجاد تست‌های واحد (C و C++)

AOSP شامل ماکروهای مقدار پرچم برای تست‌های C و C++ است که در چارچوب GoogleTest نوشته شده‌اند.

  1. در منبع تست خود، تعاریف ماکرو و کتابخانه‌های تولید شده توسط aconfig را وارد کنید:

    #include <flag_macros.h>
    #include "android_cts_flags.h"
    
  2. در منبع تست خود، به جای استفاده از ماکروهای 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 مجموعه‌ای از feature flags را تعریف می‌کند که باید شرایط غیرفعال بودن را داشته باشند. می‌توانید این flags را در ماکروهای 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) یک ماکرو است که برای پرچم‌هایی که به طور پیش‌فرض در پیکربندی دستگاه تنظیم شده‌اند، استفاده می‌شود.
  3. در فایل ساخت 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"],
      ...
    }
    
  4. تست‌ها را به صورت محلی با این دستور اجرا کنید:

    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 استفاده کنید.

مراحل زیر به شما نشان می‌دهد که چگونه یک تست سرتاسری یا تست واحد ایجاد و اجرا کنید که در آن مقادیر پرچم قابل لغو نباشند:

  1. در کد تست خود، CheckFlagsRule برای اعمال فیلترینگ تست استفاده کنید. همچنین، از حاشیه‌نویسی‌های جاوا 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() {}
    }
    
  2. کتابخانه‌های jflag-unit و aconfig-generated را به بخش static_libs فایل build برای تست خود اضافه کنید:

    android_test {
        name: "FlagAnnotationTests",
        srcs: ["*.java"],
        static_libs: [
            "androidx.test.rules",
            "my_aconfig_lib",
            "flag-junit",
            "platform-test-annotations",
        ],
        test_suites: ["general-tests"],
    }
    
  3. برای اجرای تست به صورت محلی از دستور زیر استفاده کنید:

    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 اصلی آن بازیابی می‌کند، به طوری که روی سایر متدها و کلاس‌های تست عوارض جانبی نداشته باشد.