2010-01-08 22 views
14

Soy bastante nuevo en las pruebas unitarias y actualmente estoy experimentando un poco con las herramientas de prueba de Visual Studio.Cómo modelar concurrencia en pruebas unitarias?

Mi pregunta es cómo definir las afirmaciones sobre comportamiento concurrente en estas pruebas. P.ej. dada una clase BoundedChan<T> implementar un canal limitado, ¿cómo puedo especificar pruebas como

  1. "channel.Send no bloqueará" o
  2. "Si se excede la capacidad del canal, channel.Send bloqueará hasta que un valor se lee"

¿Existe alguna solución elegante para escribir estas afirmaciones?

+1

No estoy seguro de que haya algún duplicado preciso (aunque este está cerca: http: // stackoverflow.com/questions/314580/how-do-i-perform-a-unit-test-using-threads), pero hay un buen material SO sobre esto. Enumeré algunas de las mejores discusiones en mi respuesta aquí: http://stackoverflow.com/questions/1520539/how-to-prove-that-multithreading-is-working/1520619#1520619. –

+0

@Jeff: ¿Por qué no publica esto como respuesta? Me parece bastante útil. – Dario

Respuesta

6

Aquí hay un blog article que trata el tema; se enfoca en NUnit.

Desafortunadamente, la simultaneidad sigue siendo un área de pruebas unitarias que dificulta la estructura fácilmente. No es un problema simple, y aún requiere que implemente algunos de sus propios lógica de sincronización y concurrencia en la prueba.

Para el ejemplo que proporcione, puede ser imposible escribir una prueba que conclusivamente demuestre que un método se bloqueará o no en ciertas condiciones. Es posible que pueda alcanzar cierto nivel de confianza creando primero las peores circunstancias en las que esperaría el comportamiento de bloqueo y luego escriba pruebas para determinar si eso ocurre o no.

+0

El enlace está muerto, pero Wayback Machine tiene la espalda. Artículo original de Dan Barvitsky: https://web.archive.org/web/20140822013404/http://www.atalasoft.com/cs/blogs/danbarvitsky/archive/2009/06/12/techniques-for-testing-concurrent -code-in-nunit.aspx. Su publicación de seguimiento: https://web.archive.org/web/20100526080326/http://www.atalasoft.com/cs/blogs/danbarvitsky/archive/2009/06/15/my-solution-for-unit -testing-concurrent-code-in-nunit.aspx. –

+0

@ SørenBoisen sus enlaces también están muertos :(dice que no se puede mostrar la página debido al archivo robots.txt –

+0

@BogdanMart Ouch, por lo que un robots.txt creado recientemente hará que Wayback Machine bloquee el acceso (o elimine) los rasguños del sitio anterior. Malas noticias para el archivo de Internet. Creo que las publicaciones del blog están realmente perdidas ahora. –

4

Esta pregunta podría generar suficiente contenido para llenar un libro.

En general, no recomendaría agregar pruebas unitarias a sus clases para escenarios concurrentes. Con la práctica, creo que aprenderá que las pruebas de unidades automáticas tienen uno o varios "puntos positivos", y que enfocar sus esfuerzos en estas áreas (y utilizar otras prácticas en otras áreas) produce un mejor retorno de la inversión.

Pero su clase parece ser aproximadamente concurrencia (no puede decirse con certeza en función de la información proporcionada), y por lo tanto este podría ser un caso en el que realmente desea una prueba de simulación simultánea.

Puede (hasta donde yo sé) iniciar varios hilos en una prueba de unidad si lo desea. Pero puede haber una manera más simple. Considere aprovechar el patrón Compose Method. Mientras estamos en el tema, este patrón es bastante crítico para las pruebas de unidades efectivas sobre todo, no solo con la concurrencia.

1

He creado un ayudante que puede iniciar un haz de hilos para ejecutar acciones simultáneas. El helper proporciona primitivas de sincronización y mecanismos de registro. Se puede usar junto con cualquier marco de prueba de unidad. Aquí es un fragmento de código de una unidad de prueba:

[Test] 
public void TwoCodeBlocksInParallelTest() 
{ 
    // This static method runs the provided Action delegates in parallel using threads 
    CTestHelper.Run(
     c => 
      { 
       Thread.Sleep(1000); // Here should be the code to provide something 
       CTestHelper.AddSequenceStep("Provide"); // We record a sequence step for the expectations after the test 
       CTestHelper.SetEvent(); 
      }, 
     c => 
      { 
       CTestHelper.WaitEvent(); // We wait until we can consume what is provided 
       CTestHelper.AddSequenceStep("Consume"); // We record a sequence step for the expectations after the test 
      }, 
     TimeSpan.FromSeconds(10)); // This is a timeout parameter, if the threads are deadlocked or take too long, the threads are terminated and a timeout exception is thrown 

    // After Run() completes we can analyze if the recorded sequence steps are in the correct order 
    Expect(CTestHelper.GetSequence(), Is.EqualTo(new[] { "Provide", "Consume" })); 
} 

Se puede utilizar para probar cliente/servidor o de sincronización en componentes o simplemente ejecutar un hilo con un tiempo de espera. Continuaré mejorando esto en las próximas semanas. Aquí está la página del proyecto: Concurrency Testing Helper