Comment écrire des tests unitaires pour les robots (et autres appareils mécaniques)?

22

Je suis membre du club de robotique de mon lycée et je suis responsable de la programmation du robot. Une suggestion que je continue d'entendre de la part de divers adultes est que je devrais écrire des tests unitaires pour aider à valider mon code. La base de code devient un peu grosse, et je suis d'accord que les tests unitaires seraient vraiment utiles pour m'aider à détecter les bogues plus rapidement.

Cependant, je ne sais pas exactement comment je pourrais y parvenir. À ma connaissance, le test unitaire se fait en prenant une fonction (ou un sous-système du code) et en lui fournissant un ensemble d'entrées pour s'assurer qu'il sort avec la même sortie à chaque fois. Le code que j'ai actuellement ne fait pas de gros calculs, mais manipule plutôt directement les composants matériels du robot. La plupart de la complexité vient du fait de s'assurer que l'électronique est saine, que le code correspond actuellement au matériel réel sur le robot, etc. Souvent, je peux seulement voir s'il y a un problème en chargeant le code sur le robot lui-même, et tenter de l'exécuter.

Par extension, comment écrire des tests unitaires pour le code destiné à faire fonctionner n'importe quel appareil mécanique? Il me semble que vous ne pouvez détecter les erreurs qu'en observant physiquement le fonctionnement de la machine.

Ou suis-je simplement en train de mal comprendre comment les tests unitaires devraient fonctionner?

( Si c'est important, voici le code , il est écrit en C ++, et je participe à FRC )

Michael0x2a
la source

Réponses:

21

Vous devrez simuler la couche matérielle pour effectuer ce test. L'idée est qu'au lieu que votre code parle au matériel réel, vous parlez à une fausse version du matériel que vous pouvez contrôler et ensuite utiliser pour vérifier que votre code fonctionne correctement.

Malheureusement, vous devez surmonter certains problèmes:

  • Se moquer des choses dans des langues de bas niveau est plus difficile (et donc, beaucoup plus de travail)
  • Se moquer de choses au niveau matériel est plus difficile (et donc, beaucoup plus de travail)

En outre, l'essentiel de la valeur des tests unitaires automatisés provient de la possibilité d'exécuter vos tests à tout moment pour détecter les bogues de régression pendant de longues périodes après avoir écrit le code d'origine. Avec ce genre de concours, votre code ne sera plus utilisé du tout après la fin du concours, vous n'obtiendrez donc pas vraiment la plus grande partie de la valeur des tests. Et compte tenu de la difficulté d'ajouter des tests à votre cas particulier, il serait peut-être préférable d'utiliser votre temps pour effectuer des tests manuels et vous concentrer sur les fonctionnalités du concours.

Oleksi
la source
1
Bonne réponse. Surtout le fait que le code ne sera pas utilisé après la compétition, et que le grand avantage des tests unitaires automatisés vient bien après que les tests ont été écrits. Vous pourriez envisager d'automatiser certains de vos tests dans le cas où vous vous retrouvez à répéter le même test encore et encore; mais jusqu'à ce que cela se produise, il n'y a pas beaucoup d'intérêt.
Dawood dit de réintégrer Monica le
Pas besoin de se moquer du matériel du tout. Si le robot est connecté, exécutez le programme de test et observez les journaux. Le test final doit être observé "tourner à gauche" dans le journal devrait correspondre au robot tournant à gauche. Vous devrez écrire un faisceau de test pour simuler les périphériques d'entrée - accrocher le code du périphérique d'entrée aussi près que possible de la couche matérielle
mattnz
4
@DavidWallace Comme un petit sujet de réflexion, lors de l'utilisation de TDD / BDD, les avantages des tests unitaires se produisent immédiatement. En premier lieu en permettant au code d'être refactorisé en toute confiance immédiatement, et en deuxième lieu en encourageant la mise en œuvre à se limiter à la mise en œuvre minimale nécessaire pour satisfaire aux tests.
S.Robins
4
@mattnz mauvaise idée, et je sais par expérience. Et si le code testé échoue très très fort et que le bras de robot s'écrase contre le mur, ruinant un morceau de matériel xxxx $ ???
stijn
2
@mattnz: Nos robots mesurent environ 2 pieds sur 3 pieds sur 4 pieds et pèsent environ 150 livres. Le kit / enregistrement coûte 5 mille dollars chaque année, et nous collectons généralement 5 à 10 000 dollars supplémentaires pour acheter des pièces supplémentaires. Le pire des scénarios coûterait probablement plus de 10 $;)
Michael0x2a
10

Je peux penser à deux ou trois choses que vous devrez considérer. La première consiste à rendre votre couche d'accès matériel aussi fine que possible, même si cela signifie créer pour elle une couche de type wrapper de base. Cela vous offre quelques avantages. Le premier est qu'il vous permet d'isoler les comportements spécifiques au matériel de votre code de l'accès matériel lui-même, ce qui signifie que vous pouvez tout tester jusqu'au bas de vos communications matérielles sans avoir besoin d'accéder au matériel lui-même.

Par exemple, si vous devez concevoir un protocole de signalisation basé sur I2C, vous pouvez tester que votre code génère les signaux I2C corrects sans avoir besoin de connecter le matériel à vos tests.

Pour les appels vers le matériel réel, vous pouvez tester qu'ils se comportent correctement en se moquant de votre couche matérielle, et c'est là que le maintien d'une couche matérielle très mince est vraiment payant, car vous pouvez réduire votre simulation en n'ayant besoin que de gérer les fonctions minimales requises pour s'adresser en fait au matériel, mais vous n'avez pas nécessairement besoin de tester les signaux individuels eux-mêmes, car toute votre signalisation aurait dû être testable à un niveau supérieur. Cela signifie alors que vous utilisez votre maquette pour vérifier que des appels sont effectués vers des méthodes matérielles spécifiques qui provoquent l'envoi de vos signaux vers le matériel. Si vous devez interroger votre matériel, votre maquette doit pouvoir déclencher des événements ou des méthodes dans votre code uniquement, car encore une fois, votre signalisation de retour doit être traitée dans une couche supérieure.

Cela correspond fondamentalement à ce qu'Oleksi a dit dans sa réponse , en ce sens qu'il est généralement plus difficile de se moquer des choses au niveau matériel, mais ce n'est pas si difficile si vous vous en tenez à la couche de code / appel minimal la plus légère possible que vous pouvez faire pour le Matériel.

Lorsque vous avez du code qui réussit tous ses tests, vous devrez tout de même exécuter une série de tests manuels afin de vous assurer que vous avez correctement tout intégré dans votre couche matérielle.

L'autre chose qui me vient à l'esprit en dehors de la moquerie et de la superposition, est d'utiliser une pratique de développement test-first. Essentiellement, vous codez vos exigences en tant que critères de test et vous basez votre implémentation sur vos tests. Cela vous aidera à vous assurer de garder votre code d'implémentation au minimum, tout en vous assurant que tous vos cas de test conduisent vos efforts de développement. En ne perdant pas trop de temps sur un autre code potentiellement non critique que vous pourriez être tenté de faire "juste parce que", le test vous aide d'abord à rester concentré et facilitera la modification de votre code lors du débogage, tout comme l'utilisation de vos tests unitaires et simulacres. Le débogage de bogues logiciels à travers le matériel est notoirement compliqué et absorbe une grande partie de votre temps que vous trouveriez mieux consacré à d'autres tâches.

S.Robins
la source
2

Je peux vous dire comment ils le font sur les simulateurs de vol

Tout d'abord, vous n'obtiendrez la moitié de la réponse que si vous posez cette question aux seuls programmeurs, vous devriez donc probablement la poster sur http://electronics.stackexchange.com pendant que vous y êtes.

Je n'ai pas travaillé avec des robots mais j'ai passé 5 ans à faire du matériel sur des simulateurs de vol, donc je peux vous dire comment fonctionne leur architecture.

La couche matérielle est stupide

Il contient une interface de base où vous pouvez ajuster des valeurs d'entrée / sortie simples et définir des points d'arrêt d'interpolation pour les signaux analogiques. Lorsque vous travaillez avec du matériel «neuf», tout fonctionnera comme prévu avec peu ou pas d'étalonnage, mais au fil du temps, les pièces subiront une usure mécanique et devront être ajustées.

L'étalonnage est un tableau simple qui contient des sections réparties entre les valeurs min / max. Pour mesurer l'entrée sur ceux-ci, un servo est généralement utilisé (ex: potentiomètre linéaire, transducteur, accéléromètres, etc.). Ou dans le cas de l'instrumentation, vous jugez simplement la précision visuellement et ajustez jusqu'à ce qu'il soit calibré.

La couche logicielle est l'opposé

Tout est complexe et interconnecté, il est donc important d'isoler certaines variables pour tester la fonctionnalité. Il n'est pas nécessaire de vous donner la tête en pensant à des scénarios, car il est beaucoup plus facile d'exécuter des scénarios réalistes où vous pouvez collecter des données. Lorsque vous exécutez les tests, vous mesurez essentiellement les données stockées par rapport à la sortie actuelle.

Sur un simulateur de vol, on parle de QTG (Qualification Test Guide). À sa base, il trace les données sur une grille 2D où une dimension est le temps et l'autre est la sortie.

Croyez-le ou non, c'est l'essence même de la façon dont ils développent les modèles. Un véritable avion est équipé d'une tonne de capteurs et survole des scénarios contrôlés. Étant donné que tous les contrôles peuvent être pilotés sans interaction humaine, les tests sont exécutés (c'est-à-dire que la sim vole elle-même) par l'ordinateur et les données sont comparées.

Même si la robotique est créée à une échelle très différente, les principes sont les mêmes. L'approche traditionnelle consiste à séparer complètement les couches matérielles et logicielles afin que les deux puissent être testés individuellement. L'entrée matérielle est collectée via des servos et définie via une interface indépendante. L'entrée du logiciel peut être définie / lue en mesurant et en comparant indépendamment la signalisation qui irait autrement au matériel et en la comparant à de «bonnes» données connues.

Les tests eux-mêmes n'ont pas nécessairement besoin d'être complexes tant que les résultats sont prévisibles, mesurables et reproductibles.

Plie d'Evan
la source
1

Comme indiqué précédemment, simulez et découpez les pièces de quincaillerie. Par exemple, si vous avez une interface avec le robot, vous pouvez hériter de cette interface, puis en faire de simples implémentations de stub. Ensuite, vous pouvez tester que l'implémentation du stub a été appelée comme prévu. S'il s'agit de fonctions ou de paramètres attendus.

martiert
la source