ایجاد آزمون سیستم SDV

آزمایش‌های سیستم به هر آزمایش SDV ایجادشده بااستفاده از «چارچوب آزمایش SDV» اشاره دارد.

ایجاد آزمایش

مکان آزمایش

  • <test_repository_root>/sample_tests
  • <test_repository_root>/e2e_tests
  • <test_repository_root>/long_running_tests
  • <test_repository_root>/performance_tests
  • <test_repository_root>/hardware

فایل‌های آزمایشی

  • README.md: همه آزمایش‌ها باید شامل شرحی از هدف آزمایش و نحوه اجرای آزمایش باشند.

  • فایل آزمایشی: همه آزمایش‌ها از ساختار مشابهی پیروی می‌کنند:

"""SDV Name Test"""

from sdv_test_fw.test_execution import sdv_base_test, sdv_test_runner

class SdvTypeNameTest(sdv_base_test.SdvBaseTestClass):

    def setup_class(self):
      # Setup code. Executed only once at the beginning of the test.
      super().setup_class()
      self.sdv_device1 = self.get_device('device1')
      self.sdv_device2 = self.get_device('device2')
      ...

    # Remove if not needed.
    def setup_test(self):
      super().setup_test()
      # Setup code. Executed before every test case.
      # Remove override if not needed.

    # Remove if not needed.
    def teardown_test(self):
      # Cleanup code. Executed after every test case.
      super().teardown_test()

    # Remove if not needed.
    def teardown_class(self):
      # Cleanup code. Executed once at the end of the test.
      super().teardown_class()

    def test_name_case1(self):
      # Test case step
      # Test case verification

    def test_name_case2(self):
      # Test case step
      # Test case verification

if __name__ == '__main__':
    # Start Test Execution Using SDV Test Framework
    sdv_test_runner.run()
  • فایل ساخت: Android.bp. ساختار فایل به‌صورت زیر است:
python_test_host {
    name: "SdvTypeNameTest", // Should match the name of the test class.
    main: "sdv_type_name_test.py",
    srcs: [
        "sdv_type_name_test.py",
    ],
    data: [
        ":sdv_test_fw_device_configs",
    ],
    test_options: {
        unit_test: false,
    },
    defaults: [
        "sdv_test_fw_defaults",
    ],
    test_config_template: ":<DEFAULT_TEMPLATE_NAME>",
}

قرارداد نام‌گذاری

برای شناسایی و یافتن انواع مختلف آزمایش‌ها، آزمایش‌ها باید با پیروی از یک قرارداد نام‌گذاری خاص ایجاد شوند:

  • آزمایش نمونه:
    • نام فایل: sdv_sample_<NAME>_test.py
    • نام کلاس: SdvSampleNameTest
  • آزمایش E2E:
    • نام فایل: sdv_e2e_<NAME>_test.py
    • نام کلاس: SdvE2ENameTest
  • آزمایش طولانی‌مدت:
    • نام فایل: sdv_long_running_<NAME>_test.py
    • نام کلاس: SdvLongRunningNameTest
  • آزمون عملکرد:
    • نام فایل: sdv_performance_<NAME>_test.py
    • نام کلاس: SdvPerformanceNameTest
  • آزمایش سخت‌افزار:
    • نام فایل: sdv_hw_<NAME>_test.py
    • نام کلاس: SdvHWNameTest

رهنمودهای کد

این بخش دستورالعمل‌ها و روال‌های مطلوب را برای نوشتن «آزمایش‌های سیستم SDV» ارائه می‌دهد.

Python و Mobly

با راهنمای سبک Python و روال‌های مطلوب Mobly آشنا شوید و توصیه‌های خاص SDV زیر را درنظر بگیرید:

  • از استفاده مستقیم از واردات Mobly به‌جز برای ادعاها خودداری کنید. «چارچوب آزمون SDV» با تمرکز بر SDV بر آن بنا شده است.

  • ادعاها: مستقیماً از ادعاهای Mobly استفاده کنید.

آزمایش‌های SDV

بخش‌های زیر دستورالعمل‌های خاص و روال‌های مطلوب را برای توسعه آزمایش‌ها در «چارچوب آزمایشی SDV» شرح می‌دهند.

راه‌اندازی و پاک‌سازی

کد راه‌اندازی و پاک‌سازی باید خارج از چارچوب‌های تست باشد. روش‌های Teardown حتی درصورت ناموفق بودن آزمایش فراخوانده می‌شوند تا پاک‌سازی دستگاه به‌درستی انجام شود.

مکان کد راه‌اندازی و تخریب به نیازهای خاص آزمایش بستگی دارد، حتی اگر آزمایش قطع شود:

  1. برای اجرا فقط یک‌بار در ابتدا و انتهای کل آزمایش، از setup_class و teardown_class استفاده کنید. برای مثال، دریافت دستگاه‌ها، تنظیم مقادیر متغیر یا وضعیتی که بین چارچوب‌های تست تغییر نمی‌کند، پیکربندی ویژگی‌های مشترک دستگاه، یا تنظیم پرچم‌ها.
def setup_class(self):
  super().setup_class()
  # setup code

def teardown_class(self):
  # teardown code
  super().teardown_class()
  1. برای اجرا بین چارچوب‌های تست، قبل و بعداز هرکدام از آن‌ها. برای مثال، جلسه تعاملی یا اجرای سرویس مشترک.
def setup_test(self):
  super().setup_test()
  # setup code

def teardown_test(self):
  # teardown code
  super().teardown_test()

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

وقتی مراحل در چارچوب‌های تست مختلف مشترک است، از چارچوب‌های تست پارامتری‌شده استفاده کنید تا از تکرار کد جلوگیری شود.

from absl.testing import parameterized

@parameterized.named_parameters(
  {
      'testcase_name': 'ab',
      'input1': 'a',
      'input2': 'b',
  },
  {
      'testcase_name': 'cd',
      'input1': 'c',
      'input2': 'd',
  },
)
def test_name(self, input1, input2):
  # test

این مثال دو چارچوب تست test_name_ab و test_name_cd ایجاد می‌کند.

یک چارچوب تست برای درستی‌سنجی رفتار

چارچوب‌های تست باید فشرده باشند و بر یک رفتار خاص تمرکز کنند. اگر چند رفتار پیش‌شرط‌ها یا مراحل مشترکی دارند، آن‌ها را تقسیم کنید. می‌توانید از setup_test یا پارامترسازی برای به‌حداقل رساندن مقدار کد تکراری استفاده کنید.

دنبال کردن این رویکرد خواندن و اشکال‌زدایی آزمایش‌ها را آسان‌تر می‌کند زیرا به‌وضوح نشان می‌دهد کدام مراحل و شرایط ناموفق بوده‌اند.

مثال
def test_verify_process(self):
  self.device.start_process()

  # precondition 1
  self.device.send_signal1()
  # verification signal1 received
  ...

  # precondition 2
  self.device.send_signal2()
  # verification signal2 received
  ...

  # precondition 3
  self.device.start_agent()
  # verification behavior
  ...

  self.device.kill_process()
def setup_test(self):
  super().setup_test()
  self.device.start_process()

def test_signal1(self):
  # precondition
  self.device.send_signal1()
  # verification signal1 received
  ...

def test_signal2(self):
  # precondition
  self.device.send_signal2()
  # verification signal2 received
  ...

def test_agent(self):
  # precondition
  self.device.start_agent()
  # verification behavior
  ...

def teardown_test(self):
  self.device.kill_process()
  super().teardown_test()

رفتار آزمایشی قطعی

از افزودن شرطی‌ها در آزمایشی که رفتار آن را منشعب می‌کند خودداری کنید. اگر درستی‌سنجی باید تقسیم شود، از دو چارچوب تست متفاوت استفاده کنید.

از استثنائات استفاده نشود

آزمایش‌ها و کمک‌کننده‌های رایج باید به‌جای استثناها از ادعاها استفاده کنند. این کار اشکال‌زدایی را تسهیل می‌کند و از الگوهای آزمایشی پیروی می‌کند.

مثال
result = self.some_calculations()
if result is None:
  raise Exception("No result")
result = self.some_calculations()
self.get_test_validator().assert_is_not_none(result)
مثال
if not self.device.is_subprocess_running(
  self.EXPECTED_PROCESS
):
  raise Exception("Process is not running")
self.get_test_validator().assert_true(
  self.device.is_subprocess_running(self.EXPECTED_PROCESS),
  "Process is not running"
)

از خواب استفاده نشود

از sleep استفاده نکنید زیرا زمان اجرای آزمایشی را افزایش می‌دهد و باعث ناپایداری می‌شود.

وقتی آزمایش نیاز به انتظار برای رویداد یا درستی‌سنجی برای ادامه دارد، به‌جای آن از «روش‌های انتظار» ارائه‌شده در چارچوب استفاده کنید.

از روش‌های انتظار با دقت استفاده کنید زیرا اجرای آزمایشی تا زمانی که شرط مطابقت داشته باشد یا زمان‌اتمام برسد، مسدود می‌ماند.

وقتی لازم است منتظر بمانید تا شرطی در آزمون تکمیل شود، سؤالات زیر را بپرسید:

  1. زمان استراحت معقول چیست؟

    اگر رویدادی در چارچوب زمانی خاصی انتظار می‌رود، زمان‌اتمام باید با آن انتظار مطابقت داشته باشد تا اطمینان حاصل شود که آزمایش به‌سرعت ناموفق می‌شود. درصورت لزوم، زمان وقفه را کاهش دهید (پیش‌فرض ۳۰ ثانیه است).

  2. عملیات انجام‌شده توسط روش انتظار چقدر پرهزینه است؟

    از فراخوانی مکرر عملیات پرهزینه خودداری کنید. درصورت لزوم، فاصله نظرسنجی را افزایش دهید (پیش‌فرض ۰٫۵ ثانیه است).

چارچوب‌های تست با الزامات

اگر آزمایشی دارای چارچوب‌های تست با الزامات صریح برای اجرا باشد (برای مثال، هدفی که باید در آن اجرا شود)، درصورتی‌که با الزامات مطابقت نداشته باشند، می‌توانید از آن‌ها رد شوید:

def test_with_requirement(self):
  self.get_test_validator().skip_if(expr, reason)
  # Test case