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 テスト フレームワーク内でテストを開発するための具体的なガイドラインとベスト プラクティスについて説明します。

設定とクリーンアップ

セットアップ コードとクリーンアップ コードはテストケースの外に記述する必要があります。テストが失敗した場合でも、適切なデバイス クリーンアップを行うために、ティアダウン メソッドが呼び出されます。

セットアップ コードと破棄コードの場所は、テストが中断された場合でも、テストの具体的なニーズによって異なります。

  1. テスト全体で 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

この例では、2 つのテストケース 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()

決定論的なテスト動作

動作を分岐させる条件をテストに追加しないでください。検証を分割する必要がある場合は、代わりに 2 つの異なるテストケースを使用します。

例外を使用しない

テストと共通ヘルパーでは、例外ではなくアサーションを使用する必要があります。これにより、デバッグが容易になり、テストパターンに沿ってテストできます。

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