¿Es una buena idea medir el rendimiento de un método utilizando el tiempo de espera de la prueba unitaria?

14

En un proyecto donde hay requisitos no funcionales que especifican el tiempo máximo de ejecución para una acción específica, el control de calidad debe verificar el rendimiento de esta acción en una máquina dedicada utilizando hardware preciso bajo carga precisa, tanto el hardware como la carga se especifican en los requisitos.

Por otro lado, algunos cambios erróneos en el código fuente pueden afectar gravemente el rendimiento. Notar este impacto negativo antes de tiempo , antes de que el código fuente alcance el control fuente y sea verificado por el departamento de control de calidad, podría ser beneficioso en términos de tiempo perdido por el departamento de control de calidad que informa el problema, y ​​por el desarrollador que lo corrige varias confirmaciones más tarde.

Para hacer esto, es una buena idea:

  • Para usar las pruebas unitarias para tener una idea del tiempo empleado en ejecutar la misma acción² n veces,

  • ¿Para usar el tiempo de espera por prueba a través del [TestMethod, Timeout(200)]atributo en C #?

Espero varios problemas con este enfoque:

  • Conceptualmente , las pruebas unitarias no son realmente para eso: se espera que prueben una pequeña parte de un código, nada más: ni la verificación de un requisito funcional, ni una prueba de integración, ni una prueba de rendimiento.

  • ¿El tiempo de espera de las pruebas unitarias en Visual Studio mide realmente lo que se espera medir, teniendo en cuenta que la inicialización y la limpieza no existen para esas pruebas o son demasiado cortas para afectar los resultados?

  • Medir el rendimiento de esta manera es feo. Ejecutar un punto de referencia en cualquier máquina¹ independientemente del hardware, la carga, etc. es como hacer un punto de referencia que muestre que un producto de base de datos siempre es más rápido que otro. Por otro lado, no espero que esas pruebas unitarias sean un resultado definitivo, ni algo que utilice el departamento de control de calidad . Esas pruebas unitarias se usarán solo para dar una idea general sobre el rendimiento esperado, y esencialmente para alertar al desarrollador de que su última modificación rompió algo, afectando gravemente el rendimiento .

  • Test Driven Development (TDD) es imposible para esas pruebas. ¿Cómo fallaría, en primer lugar, antes de comenzar a implementar el código?

  • Demasiadas pruebas de rendimiento afectarán el tiempo requerido para ejecutar las pruebas, por lo que este enfoque se limita solo a acciones cortas.

Teniendo en cuenta esos problemas, todavía me parece interesante usar tales pruebas unitarias si se combina con las métricas de rendimiento real del departamento de control de calidad.

¿Me equivoco? ¿Hay otros problemas que hacen que sea totalmente inaceptable utilizar pruebas unitarias para esto?

Si me equivoco, ¿cuál es la forma correcta de alertar al desarrollador de que un cambio en el código fuente afectó gravemente el rendimiento, antes de que el código fuente llegue al control de origen y sea verificado por el departamento de control de calidad?


¹ En realidad, se espera que las pruebas unitarias se ejecuten solo en PC de desarrolladores que tengan un rendimiento de hardware comparable, lo que reduce la brecha entre las máquinas más rápidas que nunca podrán fallar la prueba de rendimiento y las máquinas más lentas que nunca lograrán pasarla.

² Por acción, me refiero a un fragmento de código bastante corto que tarda unos pocos milisegundos en ejecutarse.

Arseni Mourzenko
fuente

Respuestas:

3

También estamos utilizando este enfoque, es decir, tenemos pruebas que miden el tiempo de ejecución bajo un escenario de carga definido en una máquina determinada. Puede ser importante señalar que no los incluimos en las pruebas unitarias normales. Las pruebas unitarias son ejecutadas básicamente por cada desarrollador en una máquina de desarrollador antes de confirmar los cambios. Vea a continuación por qué esto no tiene ningún sentido para las pruebas de rendimiento (al menos en nuestro caso). En su lugar, ejecutamos pruebas de rendimiento como parte de las pruebas de integración.

Usted señaló correctamente que esto no debería descartar la verificación. No asumimos que nuestra prueba sea una prueba del requisito no funcional. En cambio, lo consideramos un mero indicador de problema potencial.

No estoy seguro acerca de su producto, pero en nuestro caso, si el rendimiento es insuficiente, significa que se requiere mucho trabajo para "arreglarlo". Por lo tanto, el tiempo de respuesta, cuando dejamos esto por completo al control de calidad, es horrible. Además, las correcciones de rendimiento tendrán graves impactos en una gran parte de la base de código, lo que hace que el trabajo de control de calidad anterior sea nulo. En definitiva, un flujo de trabajo muy ineficiente e insatisfactorio.

Dicho esto, aquí hay algunos puntos a sus respectivos problemas:

  • conceptualmente: es cierto, de eso no se tratan las pruebas unitarias. Pero siempre y cuando todos sepan que se supone que la prueba no verificará nada que QA debería hacer, está bien.

  • Visual Studio: no puedo decir nada al respecto, ya que no usamos el marco de prueba de unidad de VS.

  • Máquina: depende del producto. Si su producto es algo desarrollado para usuarios finales con máquinas de escritorio individuales personalizadas, de hecho es más realista ejecutar las pruebas en máquinas de diferentes desarrolladores. En nuestro caso, entregamos el producto para una máquina con una especificación determinada y ejecutamos estas pruebas de rendimiento solo en dicha máquina. De hecho, no tiene mucho sentido medir el rendimiento en su máquina de desarrollador de doble núcleo, cuando el cliente finalmente ejecutará 16 núcleos o más.

  • TDD: Si bien la falla inicial es típica, no es imprescindible. De hecho, escribir estas pruebas temprano hace que sirva más como una prueba de regresión que como una prueba de unidad tradicional. Que la prueba tenga éxito desde el principio no es un problema. Pero sí obtiene la ventaja de que cada vez que un desarrollador agrega una funcionalidad que ralentiza las cosas, porque no estaba al tanto del requisito de rendimiento no funcional, esta prueba TDD lo detectará. Sucede mucho, y es una respuesta increíble. Imagina eso en tu trabajo diario: escribes código, lo confirmas, vas a almorzar y cuando regresas, el sistema de compilación te dice que este código cuando se ejecuta en un entorno de carga pesada es demasiado lento. Eso es lo suficientemente bueno para mí como para aceptar que la prueba TDD no ha fallado inicialmente.

  • Tiempo de ejecución: como se mencionó, no ejecutamos estas pruebas en máquinas de desarrollador, sino como parte del sistema de compilación en una especie de prueba de integración.

Franco
fuente
3

Estoy en línea con tu pensamiento. Solo pongo mi razonamiento con flujo independiente.

1. Haga que funcione antes de hacerlo mejor / más rápido
Antes de que el código proporcione cualquier medida de rendimiento (y mucho menos garantizar), primero debe corregirse, es decir, hacer que funcione funcionalmente. Optimizar el código que es funcionalmente incorrecto no solo es una pérdida de tiempo, sino que también dificulta el desarrollo.

2. El rendimiento de un sistema solo tiene sentido en un sistema completo
Normalmente, cualquier rendimiento significativo siempre depende de una infraestructura determinada y solo debe verse bajo un sistema completo. Por ejemplo, durante la prueba simulada si el módulo recibe respuestas de un archivo de texto local, pero en el entorno de producción obtiene de la base de datos, su anterior

3. El escalado del rendimiento debe hacerse por objetivo
Una vez que tenga el sistema funcional, debe analizar el rendimiento del sistema y encontrar cuellos de botella para comprender dónde necesita escalar el rendimiento. Intentar optimizar a ciegas todos los métodos, incluso antes de conocer el rendimiento de un sistema completo, puede generar una cantidad inútil de trabajo (métodos de optimización que no importan) y puede crear su código innecesariamente inflado.

No estoy al tanto de la funcionalidad de Visual Studio, pero generalmente necesita una herramienta de creación de perfiles más amplia.

Dipan Mehta
fuente
2

Tuve una tarea similar hace algún tiempo y la solución final estaba en algún punto intermedio entre las pruebas unitarias y las pruebas de rendimiento automatizadas completas.

Algunas consideraciones en ningún orden en particular, que pueden ser útiles:

  • Las pruebas de rendimiento realizadas por QA fueron laboriosas y tenían su propio horario (por ejemplo, una vez en la iteración), por lo que no fue un problema obtener el control de origen.
  • Nuestro sistema era grande y modular, las pruebas unitarias eran demasiado granulares para nuestras necesidades, y hemos creado pruebas unitarias especiales "gordas" cuidadosamente diseñadas para provocar problemas de rendimiento en las áreas específicas de interés (también se clasificaron, pero esto es un detalle de implementación).
  • Las restricciones habituales para las pruebas unitarias todavía se aplican: deben ser pequeñas, rápidas y precisas.
  • Para excluir la influencia del marco de prueba, los ejecutaba un contenedor especial, por lo que sabíamos exactamente cuánto tiempo lleva la operación dada.
  • Es posible escribirlos antes de que se complete la implementación real (los resultados pueden ser irrelevantes o útiles, dependiendo del proceso, tal vez los desarrolladores aún estén experimentando con la implementación y desearían ver cómo va en general).
  • Fueron ejecutados por el servidor CI después de cada compilación, por lo que el tiempo de ejecución total debería mantenerse relativamente corto (si esto no es así, se vuelve considerablemente más difícil determinar el cambio exacto que provocó el problema).
  • El servidor CI era potente y tenía su hardware reparado, por lo que contamos esto como una máquina dedicada (es posible usar un servidor realmente dedicado usando un agente de compilación remoto).
  • El contenedor de prueba recopiló toda la información relevante (especificaciones de hardware, nombres / categorías de prueba, carga del sistema, tiempo transcurrido, etc.) y la exportó como informes o a la base de datos.
  • Hemos tenido un dispositivo para que JIRA extraiga esos informes y dibuje gráficos agradables por nombre / categoría / número de compilación con algunos controles (superposición de la versión anterior a la actual, etc.), para que los desarrolladores puedan ver rápidamente su impacto y los gerentes puedan obtener una visión general (algo de rojo, todo verde, ya sabes, es importante para ellos).
  • Fue posible analizar cómo va el proyecto en el tiempo utilizando las estadísticas recopiladas.

Entonces, al final, teníamos un sistema escalable, flexible y predecible que podemos ajustar rápidamente para nuestros requisitos especiales. Pero requirió algún esfuerzo para implementar.

Volviendo a las preguntas. Conceptualmente , las pruebas unitarias no son para eso, pero puede aprovechar las características de su marco de prueba. Nunca he considerado los tiempos de espera de prueba como un medio para medir, es solo una red de seguridad para colgar y esas cosas. Pero si su enfoque actual funciona para usted, entonces continúe usándolo, sea práctico. Siempre puedes ponerte elegante más tarde si surge la necesidad.

Oleg Kolosov
fuente
0

Creo que lo estás haciendo bien. Este es exactamente el punto de tener tiempos de espera de prueba unitarios: para verificar si algo está sucediendo , mucho más de lo que debería. Existen limitaciones para este enfoque, pero parece que ya las conoce, por lo tanto, mientras tenga en cuenta esas limitaciones, no veo ningún problema.

Mike Baranczak
fuente