mejor práctica cuando pruebas unitarias para desarrollo integrado

45

Estoy buscando algunas estrategias de mejores prácticas para el código de prueba de unidad escrito para el sistema embebido. Por sistema incrustado, me refiero a códigos como controladores de dispositivos, controladores ISR, etc., cosas que están bastante cerca del metal.

La mayoría de las pruebas unitarias no son posibles sin probarlo en el hardware con la ayuda de un ICE. A veces, la unidad integrada también debe conectarse a otros estímulos, como interruptores mecánicos, motores paso a paso y bombillas. Esto generalmente ocurre de forma manual, la automatización sería excelente, pero difícil y costosa de lograr.

Actualizar

Encontré un marco de prueba C que parece tener bastante éxito en la prueba de proyectos integrados. Utiliza las ideas de burlarse de hardware. Echa un vistazo a Unity , CMock y posiblemente Ceedling .

Actualización 06Jul2016

Se encontró con cmocka - parece ser trabajado más activamente en.

tehnyit
fuente
1
En circunstancias similares, fuimos por Cmocka
Mawg
He escrito un tutorial muy completo sobre el tema: Prueba de unidad de aplicaciones C incrustadas con Ceedling
Dmitry Frank

Respuestas:

28

Me alejaría de las dependencias de hardware en el primer paso posible, y construiría el sistema en arneses de prueba / emulación de software, permitiendo todo tipo de marcos de prueba. A menudo, mi PC de desarrollo se usaba para probar hasta el 95% o más del sistema completo. El costo de la sobrecarga adicional (otra capa de abstracción) fue fácilmente recuperado por el código más limpio generado como resultado de esa abstracción.

La prueba de las partes verdaderamente desnudas de metal de un sistema embebido suele ser una aplicación separada (¿Prueba de unidad?) Que afecta el firmware mucho más allá de lo que las aplicaciones pueden esperar lograr. La automatización se puede hacer a un costo, pero no es típico.

A menos que tenga el presupuesto para construir un arnés de hardware de prueba de unidad que incluya ICE completo. Esto está absolutamente bien ya que generalmente las pruebas funcionales son pequeñas.

Mattnz
fuente
Esto combinado con la respuesta de jonathan cline ieee es una estrategia exitosa. Use la abstracción para hacer que la mayoría del código sea comprobable, y usando un marco de prueba simple, pruebe los bits no abstraíbles en el hardware real. Personalmente he visto este trabajo con múltiples plataformas.
Tim Williscroft
3
Cada producto que fabricamos tiene una capa de abstracción de hardware con implementación de controladores para la plataforma de destino y la PC. Esto nos permite ejecutar pruebas unitarias fácilmente. Otras ventajas: también podemos ejecutar pruebas rápidas del sistema y desarrollar la mayoría del software sin ningún hardware (como estará disponible más adelante).
MaR
15

Una herramienta necesaria para desarrollar es un inyector de señal. El sistema integrado tendrá alguna forma de interactuar con un sistema host (generalmente a través de un puerto serie reservado para la depuración). Use esto para enviar datos de prueba (la mejor opción es un formato ascii conciso para que los humanos también lo simulen fácilmente).

Estoy completamente en desacuerdo con esta parte de su pregunta: "la automatización sería excelente, pero difícil y costosa de lograr".

Usando TeraTerm como un inyector de señal de puerto serie y escribiendo algunas macros de TeraTerm (toma aproximadamente 20 minutos), hay un enorme conjunto de pruebas automatizadas que se pueden ejecutar en cualquier parte de un sistema embebido, ya sea capa de controlador, O / S, capa 4-5, etc. TeraTerm: http://en.sourceforge.jp/projects/ttssh2/

Si el puerto serie no está disponible en el sistema incorporado, utilice una herramienta de hardware para convertir los datos del puerto USB / serie en señales digitales (también de bajo costo y fácil de lograr). Mientras lees esto, estoy usando una placa de microcontrolador de $ 30 (UBW: http://www.schmalzhaus.com/UBW32/ ) para probar un sistema embebido para producción, inyectando estímulo a través de macros TeraTerm que se envía por USB / serial a el microcontrolador, que ejecuta firmware modificado que ejercita las entradas digitales y monitorea las salidas digitales del sistema embebido objetivo. Junto con esto, desarrollamos un script de Python (utiliza pyserial y pexpect) para automatizar la inyección de datos y la validación de datos. Nada de eso es difícil y nada es caro. En mi experiencia, los gerentes gastan grandes sumas de dinero (como equipo de prueba de $ 30,000) cuando el equipo de prueba no tiene experiencia y no puede concebir estas soluciones fáciles; desafortunadamente, el equipo de hierro grande de uso general a menudo no incluye los casos de prueba que capturan el peor momento / etc del sistema de destino. Por lo tanto, el método económico es preferible para la cobertura de la prueba. Por extraño que parezca.

Jonathan Cline IEEE
fuente
1
Bueno, hice la declaración costosa mientras trabajo en la industria automotriz y todo debe ser determinista, repetible y generalmente requiere un par de ingenieros para desarrollarse. Además, cuando se utilizan más elementos en la cadena de prueba, el mantenimiento también se convierte en un problema. Gracias por informarnos sobre el UBW, parece una buena opción.
tehnyit
2
Simplemente no me hagas comenzar con LabView ... eso es algo horrible típicamente.
Jonathan Cline IEEE
1
Nuestros ingenieros de prueba adoran LabView, yo mismo no lo entiendo.
tehnyit
Esto está bastante cerca de lo que hago para varias pruebas, solo que uso Python y sus bibliotecas en serie. Entonces podría conectar mis pruebas de bajo nivel en los probadores de unidades de Python junto con algo como Flask / Qt para dar una interfaz fácil de usar también.
radix07
5

Este es un problema muy difícil.

De hecho, he diseñado un arnés de prueba de unidad para un sistema integrado, que permitiría simular eventos / interrupciones de hardware y controlar el momento de la ejecución (para garantizar que cubramos todos los entrelazamientos posibles debido a la concurrencia), y se necesitó un equipo de programadores de más de 2 años para implementarlo y ponerlo a trabajar. Ese proyecto es un desarrollo patentado, pero un proyecto similar (de diseño más simple) está disponible aquí .

Entonces sí, la automatización sería genial. Sí, es muy difícil y costoso de lograr. Sí, a veces tienes que hacer eso. Sin embargo, rara vez, en mi experiencia, en la mayoría de los casos, es más rápido y más barato usar los motores paso a paso y las bombillas y hacer que todo funcione manualmente.

littleadv
fuente
He descubierto que la unidad en la que está manualmente es propensa a errores, generalmente al generar el estímulo o al medir los resultados. Especialmente cierto si la prueba unitaria es complicada. Si tiene que rehacer la prueba de la unidad nuevamente, se vuelve aún más propenso a errores.
tehnyit
@tehnyit: sí, esa fue la razón por la que decidimos desarrollar el sistema de automatización. A veces las cosas no se pueden hacer manualmente, pero la prueba de la unidad debe ser exhaustiva y cubrir los problemas de tiempo. Entonces no tienes muchas opciones, pero la automatización en este nivel es algo muy costoso.
littleadv
4

Editar: mi respuesta es cercana a la de Mattnz, creo ...


Quiero relacionar este problema con otros, todas las pruebas que dependen de algo externo a su código (como el reloj del sistema, un sistema de archivos persistente o una base de datos, contactar un servicio web externo ...). Sugiero la misma política para todos ellos, aislar los dos niveles en dos capas de código.

Probar una sola operación externa

Es posible que desee probar físicamente cada operación. Verifique que el reloj del sistema proporcione la hora correcta, verifique que un archivo realmente recuerde lo que se ha escrito, verifique que un dispositivo reciba una sola operación ...

Estas pruebas:

  • debe ser lo más simple posible: ningún algoritmo, ninguna condición o bucle
  • puede depender del orden y de la máquina: por lo tanto, debe seguir un orden estricto y repetir en cada hardware
  • son en su mayoría estables en el transcurso de su proyecto, por lo que no necesita ejecutarlos con tanta frecuencia
  • entonces ejecutarlos manualmente es una opción; la automatización es aún mejor, si no demasiado compleja
  • Tenga en cuenta que lo que se está probando no es su código , es una herramienta que su código necesita ... Por lo tanto, probar esto podría ser opcional para usted, podría haberlo hecho un equipo diferente ...

Probar la lógica (código, algoritmo) que une las operaciones externas

Al tener una capa de código para realizar las operaciones externas reales, al ocultarlas detrás de una interfaz que puede burlarse fácilmente, su lógica ya no depende de los dispositivos físicos reales ...

Puede probar simplemente, como cualquier proyecto normal, ya no está en un código incrustado difícil de probar .

KLE
fuente
3

Los simuladores de CPU integrados generalmente se pueden programar para simular también hardware. Todas las tecnologías de virtualización que no sean Xen hacen eso. Pero debe escribir un código que pretenda tener algunos registros en alguna dirección física o, en x86, una dirección en el bus de E / S, y luego debe responder a las lecturas y escrituras en estas direcciones como si su software fuera físico chip cuyo control y registros de estado se estaban accediendo.

Si desea hacer esto, sugeriría modificar QEMU. Pero no sería fácil. Este tipo de cosas generalmente solo se hace cuando está diseñando un chip personalizado con un microcontrolador y algunos otros núcleos para su E / S.

El sistema de desarrollo vendido por ARM Holdings ofrece esto y es más fácil trabajar con él que piratear QEMU, pero es muy costoso.

Hay varios emuladores ARM de código abierto que ejecutan una sola subrutina, que puede llamar a otras subrutinas, que puede usar para depurar y ajustar el rendimiento de las subrutinas que no dependen del acceso al hardware. Utilicé uno de estos con gran éxito para optimizar un cifrador AES para ARM7TDMI.

Puede escribir un arnés de prueba de unidad simple en C o C ++, vincular la clase o subrutina bajo prueba con él y luego ejecutarlo en el simulador.

He estado reflexionando sobre un problema similar durante años, cómo hacer una prueba unitaria del código del kernel de Linux o Mac OS X. Debería ser posible, pero nunca lo he intentado. Una posiblemente es construir un núcleo completo en lugar de probar su código de forma aislada, con el marco de prueba de la unidad vinculado directamente a su núcleo. Luego dispararía las pruebas unitarias desde algún tipo de interfaz externa.

Tal vez sería más productivo usar una herramienta de cobertura de código, luego probar su firmware como un paquete completo a través de su interfaz externa. La herramienta de cobertura encontraría rutas de código que aún no se habían probado, por lo que podría agregar pruebas externas adicionales en un intento de obtener más cobertura.

Mike Crawford
fuente
3

Al igual que con TDD no incrustado, los objetos simulados son definitivamente tu amigo.

Mantenga la interfaz de su hardware subyacente limpia y simple para que todo lo que esté por encima del nivel más bajo se pueda burlar y le resulte mucho más fácil: si diseña su aplicación integrada teniendo en cuenta la capacidad de prueba, las pruebas siempre serán mucho más fluidas .

Además, el hecho de que no pueda realizar las pruebas en línea hasta bastante tarde en el proyecto no significa que no deba preparar un conjunto de pruebas en línea también.

Estos (inicialmente) solo deben probar los bits que no se pudieron probar fuera de línea. Claro, no es TDD (ya que está creando las pruebas por adelantado), pero su desarrollo de TDD fuera de línea debería darle una buena idea de cómo debe verse su interfaz de hardware y, por lo tanto, qué pruebas en línea debe realizar.

Además, si el desarrollo en línea cuesta mucho más que el desarrollo fuera de línea (como lo hace donde trabajo), podría ahorrarle mucho tiempo en línea al tener un conjunto de pruebas bien entendidas para ejecutar.

Mark Booth
fuente
+1 por traer objetos simulados al plato, @mark. El único problema es que garantizar la precisión de los objetos simulados, lo que significa comprender el objeto a burlar, debe ser bastante profundo. Esto es bueno ya que obliga al desarrollador a comprender el comportamiento de los objetos externos con los que está interactuando.
tehnyit
1

En el desarrollo integrado, a menudo realiza escaneos de límites para verificar que toda la aplicación (incluido el hardware) funcione. Consulte también JTAG para la depuración del sistema.

La prueba de rutinas de software puro sin enlace al hardware puede realizarse mediante un marco de prueba de unidad C estándar como Check . Pero tenga cuidado con las limitaciones de memoria (especialmente el espacio de pila, etc. en dispositivos pequeños). ¡Conozca sus contratos! También puede intentar abstraer las rutinas de software del hardware para obtener una mayor cobertura de prueba, pero esto generalmente es costoso en términos de rendimiento en dispositivos integrados como pequeños PIC o AVR. Sin embargo, puede simular puertos de hardware para lograr una mayor cobertura (y, por supuesto, también puede probar ese simulacro).

También puede intentar usar emuladores para los simuladores de circuitos o chips, pero este tipo de herramientas son caras (especialmente en combinación) y complicadas.

Halcón
fuente
De acuerdo con el escaneo de límites y JTAG, pero, debido al diseño del hardware, no siempre es posible o no está disponible.
tehnyit