Os testes de sistema se referem a qualquer teste de SDV criado usando o framework de teste de SDV.
Criação de testes
Local do teste
<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
Testar arquivos
README.md:todos os testes precisam incluir uma descrição da finalidade e de como executar o teste.Arquivo de teste:todos os testes seguem uma estrutura semelhante:
"""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()
- Arquivo de build:
Android.bp. A estrutura do arquivo é a seguinte:
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>",
}
Convenção de nomenclatura
Para identificar e encontrar os diferentes tipos de testes, eles precisam ser criados seguindo uma convenção de nomenclatura específica.
Teste de exemplo
Nome do arquivo:
sdv_sample_<NAME>_test.pyNome da turma:
SdvSampleNameTest
Teste E2E
Nome do arquivo:
sdv_e2e_<NAME>_test.pyNome da turma:
SdvE2ENameTest
Teste de longa duração
Nome do arquivo:
sdv_long_running_<NAME>_test.pyNome da turma:
SdvLongRunningNameTest
Teste de desempenho
Nome do arquivo:
sdv_performance_<NAME>_test.pyNome da turma:
SdvPerformanceNameTest
Teste de hardware
Nome do arquivo:
sdv_hw_<NAME>_test.pyNome da turma:
SdvHWNameTest
Diretrizes de código
Esta seção fornece diretrizes e práticas recomendadas para escrever testes de sistema SDV.
Python e Mobly
Conheça o guia de estilo do Python e as práticas recomendadas do Mobly, além de considerar as seguintes recomendações específicas do SDV:
Evite usar importações do Mobly diretamente, exceto para asserções. O framework de teste de SDV se baseia nisso com foco em SDV.
Declarações:use declarações do Mobly diretamente.
Testes de SDV
As seções a seguir descrevem diretrizes e práticas recomendadas específicas para desenvolver testes na estrutura de teste do SDV.
Configuração e limpeza
O código de configuração e limpeza precisa estar fora dos casos de teste. Os métodos de desmontagem são chamados mesmo que o teste falhe, para fazer a limpeza adequada do dispositivo.
O local do código de configuração e encerramento depende das necessidades específicas do teste, mesmo que ele seja interrompido:
- Para executar apenas uma vez no início e no fim de todo o teste, use
setup_classeteardown_class. Por exemplo, receber dispositivos, definir valores de variáveis ou estados que não mudam entre casos de teste, configurar propriedades comuns de dispositivos ou definir flags.
def setup_class(self):
super().setup_class()
# setup code
def teardown_class(self):
# teardown code
super().teardown_class()
- Para executar entre casos de teste, antes e depois de cada um deles. Por exemplo, uma sessão interativa ou a execução de um serviço comum.
def setup_test(self):
super().setup_test()
# setup code
def teardown_test(self):
# teardown code
super().teardown_test()
Casos de teste parametrizados
Use casos de teste parametrizados quando as etapas forem comuns em diferentes casos de teste para evitar a repetição de código.
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
O exemplo cria dois casos de teste, test_name_ab e test_name_cd.
Um caso de teste para verificação de comportamento
Os casos de teste precisam ser compactos e focar em um comportamento específico. Se vários
comportamentos compartilharem pré-condições ou etapas comuns, considere dividi-los. É possível
usar setup_test ou parametrização para minimizar a quantidade de código repetitivo.
Seguir essa abordagem facilita a leitura e a depuração dos testes porque indica claramente quais etapas e condições falharam.
Exemplo
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()
Comportamento de teste determinista
Evite adicionar condicionais no teste que ramificam o comportamento. Se uma verificação precisar ser dividida, use dois casos de teste diferentes.
Não usar exceções
Os testes e helpers comuns precisam usar declarações em vez de exceções. Isso facilita a depuração e segue padrões de teste.
Exemplo
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)
Exemplo
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"
)
Não usar o modo de espera
Evite usar sleep porque isso aumenta o tempo de execução do teste e causa instabilidade.
Quando o teste exigir que um evento ou uma verificação aguarde para continuar, use os métodos de espera fornecidos no framework.
Use métodos de espera com cuidado porque a execução do teste permanece bloqueada até que a condição seja atendida ou o tempo limite seja atingido.
Quando você precisa esperar que uma condição seja concluída em um teste, faça as seguintes perguntas:
O que é um tempo limite razoável?
Se um evento for esperado em um período específico, o tempo limite deverá corresponder a essa expectativa para garantir que o teste falhe rapidamente. Reduza o tempo limite se necessário (o padrão é 30 segundos).
Qual é o custo da operação realizada pelo método de espera?
Evite chamar operações caras com frequência. Aumente o intervalo de sondagem, se necessário.O padrão é 0,5 s.
Casos de teste com requisitos
Se um teste tiver casos com requisitos explícitos para funcionar (por exemplo, o destino em que ele deve ser executado), você poderá ignorá-los se eles não corresponderem aos requisitos:
def test_with_requirement():
self.get_test_validator().skip_if(expr, reason)
# Test case