建立 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

端對端測試

  • 檔案名稱: 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 測試架構中開發測試時,應遵守的具體準則和最佳做法。

設定和清除

設定和清除程式碼必須位於測試案例之外。即使測試失敗,系統仍會呼叫拆解方法,以便適當清理裝置。

設定和終止程式碼的位置取決於測試的具體需求,即使測試中斷也一樣:

  1. 如要在整個測試開始和結束時各執行一次,請使用 setup_classteardown_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_abtest_name_cd

一個用於驗證行為的測試案例

測試案例應簡潔,並著重於特定行為。如果多個行為共用常見的前置條件或步驟,請考慮將其分割。您可以使用 setup_test 或參數化,盡量減少重複的程式碼量。

採用這種做法可清楚指出失敗的步驟和條件,因此更容易閱讀及偵錯測試。

範例
test_verify_process():
  device.start_process()

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

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

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

  device.kill_process()
test_setup_test():
  super().setup_test()
  device.start_process()

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

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

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

teardown_test():
  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. 合理的逾時時間為何?

    如果預期在特定時間範圍內發生事件,逾時時間應符合該預期,確保測試快速失敗。如有需要,請縮短逾時時間 (預設為 30 秒)。

  2. 等待方法執行的作業有多耗費資源?

    請避免頻繁呼叫耗用資源的作業。視需要增加輪詢間隔 (預設為 0.5 秒)。

符合規定的測試案例

如果測試的測試案例有明確的工作需求 (例如應執行的目標),您可以略過不符合需求的測試案例:

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