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 )
la source
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.
la source
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.
la source
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.
la source