Под системными тестами подразумеваются любые тесты 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 и Mobile
Ознакомьтесь с руководством по стилю Python и рекомендациями Mobly, а также учтите следующие рекомендации, специфичные для SDV:
Избегайте прямого использования импорта Mobly, за исключением утверждений. Тестовая среда SDV основана на этом подходе и ориентирована на SDV.
Утверждения: Используйте утверждения Mobly напрямую.
Тесты SDV
В следующих разделах изложены конкретные рекомендации и лучшие практики разработки тестов в рамках тестовой среды SDV.
Подготовка и уборка
Код инициализации и очистки должен находиться вне тестовых случаев. Методы завершения вызываются даже в случае сбоя теста для корректной очистки устройства.
Место для размещения кода инициализации и завершения зависит от конкретных потребностей теста, даже если тест прерывается:
- Чтобы выполнить проверку только один раз в начале и в конце всего теста, используйте
setup_classиteardown_class. Например, получите список устройств, установите значения переменных или состояние, которое не меняется между тестовыми случаями, настройте общие свойства устройств или установите флаги.
def setup_class(self):
super().setup_class()
# setup code
def teardown_class(self):
# teardown code
super().teardown_class()
- Для запуска между тестовыми случаями, до и после каждого из них. Например, интерактивная сессия или выполнение стандартного сервиса.
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 или параметризацию.
Такой подход упрощает чтение и отладку тестов, поскольку четко указывает, какие шаги и условия не сработали.
Пример
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()
Детерминированное поведение при тестировании
Избегайте добавления в тест условных операторов, которые разветвляют его поведение. Если проверку необходимо разделить, используйте вместо этого два разных тестовых случая.
Не используйте исключения.
В тестах и вспомогательных функциях следует использовать утверждения (assertions) вместо исключений. Это упрощает отладку и соответствует шаблонам тестирования.
Пример
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 поскольку она увеличивает время выполнения тестов и вызывает нестабильность.
Если для продолжения теста требуется ожидание события или проверки, используйте вместо этого методы ожидания, предоставляемые фреймворком.
Используйте методы ожидания с осторожностью, поскольку выполнение теста остается заблокированным до тех пор, пока не будет выполнено условие или не истечет время ожидания.
Когда вам нужно дождаться завершения выполнения условия в тесте, задайте себе следующие вопросы:
Какое значение тайм-аута можно считать разумным?
Если событие ожидается в течение определенного промежутка времени, время ожидания должно соответствовать этому ожиданию, чтобы тест быстро завершился с ошибкой. При необходимости уменьшите время ожидания (по умолчанию — 30 секунд).
Насколько дорогостоящей является операция, выполняемая методом ожидания?
Избегайте частого вызова ресурсоемких операций. При необходимости увеличьте интервал опроса (по умолчанию — 0,5 с).
Тестовые примеры с требованиями
Если тест содержит тестовые случаи с явно заданными требованиями к выполнению (например, целевая платформа, где он должен выполняться), вы можете пропустить их, если они не соответствуют этим требованиям:
def test_with_requirement():
self.get_test_validator().skip_if(expr, reason)
# Test case