.net - para - hashtags live
Unidad probando una aplicaciĆ³n multiproceso (9)
Al igual que las pruebas de unidad GUI, esto es un poco de Waterloo para las pruebas automatizadas. Los hilos verdaderos son por definición ... impredecibles ... interferirán de formas que no se pueden predeterminar. Por lo tanto, escribir pruebas verdaderas es difícil, si no imposible.
Sin embargo, tiene algo de compañía con esto ... Sugiero buscar a través de los archivos del grupo de Yahoo de testdrivendevelopment . Recuerdo algunos mensajes en las cercanías. Aquí está uno de los más nuevos. (Si alguien tiene la amabilidad de atacar y parafrasear ... eso sería genial. Tengo demasiado sueño ... Necesito LO SO)
¿Alguien tiene algún consejo para una manera consistente de probar la unidad de una aplicación multiproceso? He hecho una aplicación donde nuestros simulacros de "hilos de trabajo" tenían un hilo. Duerma con un tiempo especificado por una variable de miembro público. Lo usaríamos para poder establecer cuánto tiempo llevaría un hilo en particular para completar su trabajo, luego podríamos hacer nuestras afirmaciones. ¿Alguna idea de una mejor manera de hacer esto? ¿Alguna buena estructura falsa para .Net que pueda manejar esto?
El primer paso es darse cuenta de que, a menudo, gran parte del código que necesita para la prueba unitaria es ortogonal al enhebrado. Esto significa que debe intentar y dividir el código que hace el trabajo desde el código que realiza el subprocesamiento. Una vez hecho esto, puede probar fácilmente el código que hace el trabajo utilizando las prácticas de prueba de unidades normales. Pero por supuesto, lo sabías.
El problema es entonces probar el lado del problema en el subprocesamiento, pero al menos ahora tiene un punto en el que este subprocesamiento interactúa con el código que hace el trabajo y con suerte tiene una interfaz que puede simular. Ahora que tiene una simulación del código al que llama el código de enhebrado, creo que lo mejor que puede hacer es agregar algunos eventos al simulacro (esto puede significar que necesita enrollar su simulacro a mano). Los eventos se usarán para permitir que la prueba se sincronice y bloquee el código de enhebrado bajo prueba.
Entonces, por ejemplo, digamos que tenemos algo realmente simple, una cola de subprocesos múltiples que procesa elementos de trabajo. Te burlarías del elemento de trabajo. La interfaz puede incluir un método ''Process ()'' que el hilo llama para hacer el trabajo. Ponerías dos eventos allí. Uno que el simulacro establece cuando se llama a Process () y uno que el simulacro espera después de haber establecido el primer evento. Ahora, en su prueba, puede iniciar su cola, publicar un elemento de trabajo simulado y luego esperar en el evento "Me estoy procesando" de la tarea. Si todo lo que está probando es que se llama al proceso, puede establecer el otro evento y dejar que el hilo continúe. Si está probando algo más complejo, como la forma en que la cola maneja despacho múltiple o algo así, entonces puede hacer otras cosas (como publicar y esperar por otros elementos de trabajo) antes de liberar el hilo. Dado que puede esperar con un tiempo de espera en la prueba, puede asegurarse de que (digamos) solo dos elementos de trabajo se procesen en paralelo, etc., etc. La clave es que las pruebas sean determinísticas usando eventos que el código enhebrado bloquea de manera que la prueba puede controlar cuando se ejecutan.
Estoy seguro de que su situación es más compleja, pero este es el método básico que uso para probar código enhebrado y funciona bastante bien. Puede tomar una cantidad sorprendente de control sobre el código de subprocesos múltiples si se burla de los bits correctos y coloca puntos de sincronización.
Aquí hay más información sobre este tipo de cosas, aunque se trata de una base de código C ++: http://www.lenholgate.com/blog/2004/05/practical-testing.html
Es importante probar el código de subprocesos múltiples en una máquina multiprocesador. Una máquina de doble núcleo puede no ser suficiente. He visto bloqueos en una máquina de 4 procesadores que no ocurrieron en un procesador de doble núcleo. Luego debe crear una prueba de estrés basada en un programa de cliente que genere muchos subprocesos y realice múltiples solicitudes en contra de la aplicación de destino. Ayuda si la máquina cliente también es multiprocesador, por lo que hay más carga en la aplicación de destino.
Me encontré con un producto de investigación, llamado Microsoft Chess . Está diseñado específicamente para pruebas no deterministas de aplicaciones multiproceso. Hasta ahora, la desventaja es que está integrada en VS.
Mi consejo sería no confiar en pruebas unitarias para detectar problemas de concurrencia por varias razones:
- Falta de reproducibilidad: las pruebas fallarán solo de vez en cuando, y no serán realmente útiles para identificar los problemas.
- La creación errática de errores molestará a todos en el equipo, ya que siempre se sospechará erróneamente que el último compromiso fue la causa de la compilación anómala.
- Es probable que los bloqueos cuando se encuentren congelen la compilación hasta que se encuentre el tiempo de espera de ejecución, lo que puede ralentizar significativamente la compilación.
- Es probable que el entorno de compilación sea un entorno de CPU único (cree que la compilación se ejecuta en una VM) donde los problemas de simultaneidad nunca pueden suceder, independientemente de cuánto tiempo de inactividad se haya establecido.
- Derrota de alguna manera la idea de tener unidades simples y aisladas de código de validación.
No creo que las pruebas unitarias sean una forma efectiva de encontrar errores de subprocesamiento, pero pueden ser una buena forma de demostrar un error conocido de subprocesamiento, aislarlo y probar su solución. También los he usado para probar las características básicas de una clase de coordinación en mi aplicación, como una cola de bloqueo, por ejemplo.
Transmití la biblioteca multithreadedTC de Java a .NET y lo llamé TickingTest . Le permite iniciar varios subprocesos desde un método de prueba de unidad y coordinarlos. No tiene todas las características del original, pero lo he encontrado útil. Lo más importante que falta es la capacidad de monitorear los hilos que se inician durante la prueba.
No es exactamente una prueba unitaria, pero podrías escribir un código de prueba que llama repetidamente al código que se ejecutará en diferentes subprocesos. Intentando crear intercalación máxima entre hilos con una comprobación de consistencia periódica o final. Por supuesto, este enfoque tiene la desventaja de no ser reproducible, por lo que necesitaría usar un registro extensivo para descubrir qué salió mal. Este enfoque se combinaría mejor con pruebas unitarias para cada tarea individual de hilos.
Si tienes que probar que un hilo de fondo hace algo, una técnica simple que considero útil es tener un método WaitUntilTrue, que se parece a esto:
bool WaitUntilTrue(Func<bool> func,
int timeoutInMillis,
int timeBetweenChecksMillis)
{
Stopwatch stopwatch = Stopwatch.StartNew();
while(stopwatch.ElapsedMilliseconds < timeoutInMillis)
{
if (func())
return true;
Thread.Sleep(timeBetweenChecksMillis);
}
return false;
}
Usado así:
volatile bool backgroundThreadHasFinished = false;
//run your multithreaded test and make sure the thread sets the above variable.
Assert.IsTrue(WaitUntilTrue(x => backgroundThreadHasFinished, 1000, 10));
De esta manera, no tiene que dormir su hilo de prueba principal durante mucho tiempo para darle tiempo al hilo de fondo para que termine. Si el fondo no termina en un tiempo razonable, la prueba falla.
TypeMock (comercial) tiene un marco de prueba de unidades que automáticamente intenta encontrar interbloqueos en aplicaciones multiproceso y creo que se puede configurar para ejecutar subprocesos con un cambio de contexto predecible.
Vi una demostración esta semana en un show, aparentemente está en Alpha (llamado Racer)
http://www.typemock.com/Typemock_software_development_tools.html