¿Hay una regla simple a seguir para determinar qué características justifican una prueba unitaria y cuáles se verifican durante las pruebas de integración?

Es muy simple: todas las funciones requieren pruebas unitarias.

Todos los componentes de software que contribuyen a una función requieren las pruebas unitarias correspondientes. En TDD, el código solo se produce en la medida necesaria para aprobar el caso de prueba automatizado escrito antes. Mantener esa granularidad lo suficientemente baja como para limitar el esfuerzo de depuración y lograr una buena cobertura de prueba significa que esas pruebas son pruebas unitarias con un poco de libertad en la definición (un componente y los componentes sobre los que se basa que son lo suficientemente rápidos y fáciles de controlar para una iteración rápida).

Más adelante en el ciclo de desarrollo, todas las correcciones de errores también deben tener pruebas unitarias, ya que escribir código requiere casos de prueba fallidos. Comienzo con el componente de nivel más bajo que no ha sido eliminado como causa. Eso puede implicar construir maquetas y / o hacer una inyección de falla.

Por ejemplo, tengo un producto de copia de seguridad en la nube empresarial que tiene una función de limitación que permite a los usuarios limitar el ancho de banda durante períodos de tiempo específicos donde la mayoría de los usuarios desean limitar el impacto en su conectividad a Internet durante el horario comercial.

Los componentes con pruebas unitarias para la característica incluyen

  1. La clase de aplicación del acelerador que traduce una sola especificación de aceleración (10 megabytes / segundo de 5pm martes a 9am miércoles) en horas absolutas de inicio y finalización basadas en la hora actual.
  2. La clase de configuración del acelerador que acepta varios períodos, comprueba las superposiciones y devuelve el objeto de aplicación actual o siguiente en función de la entrada de tiempo
  3. La clase de cubos de token del acelerador que devuelve un objeto para la siguiente operación con un tiempo de inicio programado basado en la configuración y toma los parámetros de operación más el tiempo como entrada. Esto tiene pruebas con los resultados esperados especificados por el programador y un verificador de modelos.
  4. La implementación de API enchufable del acelerador que proporciona tiempo y bloqueo del sistema que utiliza una base de tiempo sintética y detecta un punto muerto.
  5. La propia clase de aceleración que maneja múltiples operaciones en cola, bloqueo y acceso multiproceso utilizando esa API.
  6. El programa de respaldo que se ejecuta en las máquinas del cliente que utiliza la clase de aceleración con las variables de tiempo, bloqueo y condición del sistema proporcionadas por boost.

Las situaciones con condiciones de borde en múltiples capas de abstracción tienen pruebas en cada una. Por ejemplo, un período de aceleración que comienza cuando finaliza el anterior tiene pruebas en el código de configuración que no deben rechazar esa configuración + devolver períodos apropiados y la clase de cubos de fichas que deben relacionar situaciones como operaciones con tiempos de inicio en el próximo período.

Como se señaló, TDD todavía se aplica a las correcciones de errores. Cuando los chicos de QA obtuvieron una afirmación con una velocidad de aceleración de 3kbit / segundo, agregué una prueba de unidad fallida para el programa de respaldo, esa prueba sugirió que estaba en el código de tokens, y agregué un caso fallido a test_buckets.

El código del producto es de aproximadamente 3600 líneas de C ++, 61 casos de prueba de la biblioteca 2900 líneas de C ++ y 4 pruebas de nivel de proceso 50 líneas de shell excluyendo infraestructura común.

En la superficie, la relación de producto a código de prueba parece aterradora, aunque eso no es una métrica lo que importa. Le preocupa el costo total de propiedad, que es mucho más bajo cuando los clientes no encuentran errores que son difíciles de reproducir y no están iterando sobre los candidatos de lanzamiento con QA.

No prueba características en pruebas unitarias. Usted prueba componentes individuales (clases, bibliotecas) que participan en una función para ver si funcionan correctamente por sí mismos. Cuando se prueban los componentes, el siguiente paso más alto en las pruebas de software es la prueba de integración para verificar si los componentes interactúan entre sí correctamente.