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. 전체 테스트의 시작과 끝에서 한 번만 실행하려면 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() 사용 안함

sleep()은 테스트 실행 시간을 늘리고 불안정성을 유발하므로 사용하지 마세요.

테스트에서 이벤트 또는 검증이 계속될 때까지 기다려야 하는 경우 프레임워크에 제공된 대기 메서드를 대신 사용하세요.

조건이 일치하거나 제한 시간에 도달할 때까지 테스트 실행이 차단된 상태로 유지되므로 대기 메서드를 신중하게 사용하세요.

테스트에서 조건이 완료될 때까지 기다려야 하는 경우 다음 질문을 해보세요.

  1. 적절한 제한 시간은 얼마인가요?

    특정 기간 내에 이벤트가 예상되는 경우 테스트가 빠르게 실패하도록 제한 시간이 예상과 일치해야 합니다. 필요한 경우 제한 시간을 줄입니다 (기본값은 30초).

  2. 대기 메서드에서 실행하는 작업의 비용은 얼마인가요?

    비용이 많이 드는 작업을 자주 호출하지 마세요. 필요한 경우 폴링 간격을 늘립니다 (기본값은 0.5초).

요구사항이 있는 테스트 사례

테스트에 작동하기 위한 명시적인 요구사항이 있는 테스트 사례가 있는 경우 (예: 실행해야 하는 타겟) 요구사항과 일치하지 않으면 건너뛸 수 있습니다.

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