Les tests système désignent tous les tests SDV créés à l'aide du framework de test SDV.
Création de tests
Lieu du test
<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
Tester des fichiers
README.md: tous les tests doivent inclure une description de l'objectif du test et de la manière de l'exécuter.Fichier de test : tous les tests suivent une structure similaire :
"""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()
- Fichier de compilation :
Android.bp. La structure du fichier est la suivante :
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>",
}
Convention d'attribution de noms
Pour identifier et trouver les différents types de tests, ceux-ci doivent être créés selon une convention d'attribution de noms spécifique :
- Exemple de test :
- Nom du fichier :
sdv_sample_<NAME>_test.py - Nom de la classe :
SdvSampleNameTest
- Nom du fichier :
- Test E2E :
- Nom du fichier :
sdv_e2e_<NAME>_test.py - Nom de la classe :
SdvE2ENameTest
- Nom du fichier :
- Test de longue durée :
- Nom du fichier :
sdv_long_running_<NAME>_test.py - Nom de la classe :
SdvLongRunningNameTest
- Nom du fichier :
- Test de performances :
- Nom du fichier :
sdv_performance_<NAME>_test.py - Nom de la classe :
SdvPerformanceNameTest
- Nom du fichier :
- Test du matériel :
- Nom du fichier :
sdv_hw_<NAME>_test.py - Nom de la classe :
SdvHWNameTest
- Nom du fichier :
Consignes de codage
Cette section fournit des consignes et des bonnes pratiques pour l'écriture de tests système SDV.
Python et Mobly
Familiarisez-vous avec le guide de style Python et les bonnes pratiques Mobly, et tenez compte des recommandations suivantes spécifiques aux SDV :
Évitez d'utiliser directement les importations Mobly, sauf pour les assertions. Le framework de test SDV s'appuie sur ce framework, en mettant l'accent sur le SDV.
Assertions : utilisez directement les assertions Mobly.
Tests SDV
Les sections suivantes décrivent les consignes et les bonnes pratiques spécifiques pour développer des tests dans le framework de test SDV.
Configuration et nettoyage
Le code de configuration et de nettoyage doit se trouver en dehors des scénarios de test. Les méthodes de démontage sont appelées même si le test échoue, afin de nettoyer correctement l'appareil.
L'emplacement du code de configuration et de suppression dépend des besoins spécifiques du test, même si celui-ci est interrompu :
- Pour ne l'exécuter qu'une seule fois au début et à la fin de l'ensemble du test, utilisez
setup_classetteardown_class. Par exemple, obtenir des appareils, définir des valeurs de variables ou un état qui ne change pas entre les scénarios de test, configurer des propriétés d'appareil courantes ou définir des indicateurs.
def setup_class(self):
super().setup_class()
# setup code
def teardown_class(self):
# teardown code
super().teardown_class()
- Pour s'exécuter entre les scénarios de test, avant et après chacun d'eux. Par exemple, une session interactive ou l'exécution d'un service commun.
def setup_test(self):
super().setup_test()
# setup code
def teardown_test(self):
# teardown code
super().teardown_test()
Scénarios de test paramétrés
Utilisez des scénarios de test paramétrés lorsque les étapes sont communes à différents scénarios de test pour éviter la répétition du code.
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
L'exemple crée deux scénarios de test, test_name_ab et test_name_cd.
Un scénario de test pour la vérification du comportement
Les scénarios de test doivent être compacts et se concentrer sur un comportement spécifique. Si plusieurs comportements partagent des conditions préalables ou des étapes communes, envisagez de les diviser. Vous pouvez utiliser setup_test ou le paramétrage pour réduire la quantité de code répétitif.
Cette approche facilite la lecture et le débogage des tests, car elle indique clairement les étapes et les conditions qui ont échoué.
Exemple
def test_verify_process(self):
self.device.start_process()
# precondition 1
self.device.send_signal1()
# verification signal1 received
...
# precondition 2
self.device.send_signal2()
# verification signal2 received
...
# precondition 3
self.device.start_agent()
# verification behavior
...
self.device.kill_process()
def setup_test(self):
super().setup_test()
self.device.start_process()
def test_signal1(self):
# precondition
self.device.send_signal1()
# verification signal1 received
...
def test_signal2(self):
# precondition
self.device.send_signal2()
# verification signal2 received
...
def test_agent(self):
# precondition
self.device.start_agent()
# verification behavior
...
def teardown_test(self):
self.device.kill_process()
super().teardown_test()
Comportement de test déterministe
Évitez d'ajouter des conditions dans le test qui modifient son comportement. Si une validation doit être divisée, utilisez plutôt deux scénarios de test différents.
Ne pas utiliser d'exceptions
Les tests et les assistants courants doivent utiliser des assertions au lieu d'exceptions. Cela facilite le débogage et suit les modèles de test.
Exemple
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)
Exemple
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"
)
Ne pas utiliser la mise en veille
Évitez d'utiliser sleep, car cela augmente le temps d'exécution des tests et entraîne des instabilités.
Lorsque le test nécessite d'attendre un événement ou une validation pour continuer, utilisez plutôt les méthodes d'attente fournies dans le framework.
Utilisez les méthodes d'attente avec précaution, car l'exécution du test reste bloquée jusqu'à ce que la condition soit remplie ou que le délai avant expiration soit atteint.
Lorsque vous devez attendre qu'une condition soit remplie dans un test, posez-vous les questions suivantes :
Qu'est-ce qu'un délai d'expiration raisonnable ?
Si un événement est attendu dans un délai spécifique, le délai avant expiration doit correspondre à cette attente pour s'assurer que le test échoue rapidement. Réduisez le délai avant expiration si nécessaire (la valeur par défaut est de 30 secondes).
Quel est le coût de l'opération effectuée par la méthode d'attente ?
Évitez d'appeler fréquemment des opérations coûteuses. Augmentez l'intervalle d'interrogation si nécessaire (la valeur par défaut est de 0,5 s).
Scénarios de test avec exigences
Si un test comporte des scénarios de test avec des exigences explicites pour fonctionner (par exemple, la cible sur laquelle il doit s'exécuter), vous pouvez les ignorer s'ils ne correspondent pas aux exigences :
def test_with_requirement(self):
self.get_test_validator().skip_if(expr, reason)
# Test case