¿Cuál es el valor de escribir pruebas unitarias adicionales al refactorizar una función más grande en funciones más pequeñas?

8

Si tengo una función de prueba de unidad compleja :

def do_everything():
    # turn twizzles
    # push buttons
    # move mountain

Y lo vuelvo a factorizar en algunas unidades más pequeñas:

def do_everything():
    turn_twizzles()
    push_buttons()
    move_mountain()

def turn_twizzles():
    # turn twizzles

def push_buttons():
    # push buttons

def move_mountain():
    # move mountain

¿Estoy perdiendo el tiempo escribiendo pruebas de unidades adicionales para esas unidades más pequeñas?

Adam Terrey
fuente

Respuestas:

15

Supongo que ya tiene pruebas unitarias que cubren el comportamiento de do_everything()? Si ha desglosado, turn_twizzles()etc., como métodos privados, no ha cambiado ningún comportamiento externo, por lo que no necesita cambiar ninguna prueba.

Sin embargo, si turn_twizzles()se hace público, entonces ha introducido una nueva funcionalidad (como se observa desde fuera de la clase), por lo que sería valioso probar esto.

JacquesB
fuente
2
No estoy seguro de por qué esta respuesta ha sido rechazada. Es breve, al punto y 100% puntual en sus consejos.
David Arno
Esta es una respuesta muy clara, gracias. Creo que el problema (crédito a @Samuel por mencionarlo puño) es el problema del clima o no, la interfaz pública ha cambiado.
Adam Terrey
Voto negativo porque esto no está completo. Incluso si las nuevas funciones no fueran parte de una API pública, escribir pruebas puede ser muy útil. Digamos que una prueba para do_everything () falla: ¿dónde está el error? Si tiene pruebas para las tres subfunciones, será mucho más fácil de encontrar. Eso es un beneficio de escribir las pruebas y debería mencionarse aquí.
marstato
1
@marstato: En general, se considera una mala idea probar métodos privados por unidad, ya que combina las pruebas con los detalles de implementación.
JacquesB
@JaxquesB Eso no es generalmente algo malo. Se podría decir que cualquier cosa más precisa que una prueba de integración habitual está vinculada a los detalles de implementación. Pero usted escribe esas pruebas de todos modos porque lo ayudan a rastrear errores, no porque le ayuden a probar la funcionalidad correcta. El modificador de acceso es siempre subjetivo. Una privatepalabra clave en un lenguaje de programación es solo una de las muchas formas de restringir el acceso a un fragmento de código.
marstato
12

Depende. Para ser más precisos, depende de

  • La complejidad de la función original (si era muy compleja, probar cada parte por sí solo valdrá la pena)
  • la complejidad de las funciones más pequeñas (si son partes complejas por sí mismas, probarlas individualmente conducirá a pruebas más detalladas y a una detección más precisa de la causa raíz en caso de un defecto)
  • las pruebas unitarias existentes (si ya producen una cobertura suficiente para todas esas "partes", entonces probablemente valga menos la pena escribir pruebas individuales)
  • si desea mantener esas funciones más pequeñas, "detalles de implementación" de las funciones originales, o no (para la primera, escribir pruebas unitarias para las funciones más pequeñas sería contraproducente para este objetivo).

Especialmente cuando esas "funciones más pequeñas" no son tan triviales como en su ejemplo, pero tendrán una lista más o menos compleja de parámetros de entrada, puede ser realmente difícil producir suficientes pruebas unitarias para su función original para garantizar que se prueben las funciones más pequeñas con todas las combinaciones de entrada "interesantes". Esa sería una señal clara para escribir pruebas unitarias específicas para las funciones más pequeñas también.

Por lo tanto, no hay un claro "sí" o "no" a esta pregunta, es una compensación que debe decidir por caso.

Doc Brown
fuente
6

Si turn_twizzles, push_buttonsy move_mountainson públicos y son llamados por otro código, entonces creo que es importante refactorizar sus pruebas para probar estas funciones individualmente.

Por desgracia después de su refactor tiene un problema: a prueba de unidad do_everythingque necesita para ser capaz de burlarse turn_twizzles, push_buttonsy move_mountain. Escribir pruebas do_everythingsin burlarse de las dependencias será una prueba de integración, no necesariamente algo malo dependiendo de su plan de prueba, pero no habrá muchos beneficios porque ya está probando las tres funciones más pequeñas individualmente. Este puede ser el momento adecuado para rediseñar este componente y colaborar con otros objetos para hacer todo el trabajo do_everything.

Si turn_twizzles, push_buttonsy move_mountainno se llaman externamente, deberían marcarse como privados, y no recomendaría probarlos por separado do_everything. Esto se debe a que desde una de entrada fuera de aspecto perspectiva, do_everythingsería la unidad más pequeña (ya que los otros son inaccesibles). Consulte también esta respuesta sobre la separación de métodos que utilizan métodos privados.

Samuel
fuente
44
He downvoted esto como " a prueba de unidad do_everythingque necesita para ser capaz de burlarse turn_twizzles, push_buttonsy move_mountain... " depende de una definición sin sentido de "prueba de unidad".
David Arno
1
@David para ser justos, Samuel reconoce eso en la respuesta. Algo así como.
GnP
4

No. Las pruebas unitarias adicionales son más precisas. Si move_mountainfalla, entonces fallará una sola prueba que dice muy específicamente qué salió mal.

Esa precisión reduce el tiempo de depuración, lo cual es valioso. Además, dado que la prueba está más enfocada, debería ser más rápido de ejecutar que probar la misma funcionalidad a través de la función completa, proporcionando una retroalimentación más rápida, lo cual es valioso.

Telastyn
fuente