unitarias tipos software pruebas las ist ejemplo desventajas unit-testing mocking

unit testing - tipos - El valor de las pruebas unitarias de alto nivel y los objetos simulados



tipos de pruebas de software (5)

Estoy empezando a creer que la prueba de nivel alto, código bien escrito, que requiere un uso extenso de objetos simulados, tiene poco o ningún valor. Me pregunto si esta afirmación es correcta o me falta algo.

¿Qué quiero decir con alto nivel? Estas son las clases y funciones cerca de la parte superior de la cadena alimenticia. Su entrada y salida tiende a ser la entrada del usuario y la interfaz de usuario. La mayor parte de su trabajo consiste en tomar la información del usuario y hacer una serie de llamadas a entidades de nivel inferior. A menudo tienen poco o ningún valor de retorno significativo.

¿Qué quiero decir con "bien escrito"? En este caso, me refiero al código que está desacoplado de sus dependencias (utilizando interfaces e inyección de dependencia), y línea por línea se encuentra en un nivel constante de abstracción. No hay algoritmos complicados y pocos condicionales.

Odio escribir pruebas unitarias para este tipo de código. Las pruebas unitarias consisten casi por completo en la configuración de objeto simulado. Línea por línea, la prueba de la unidad se lee casi como una imagen reflejada de la implementación. De hecho, escribo las pruebas unitarias mirando la implementación. "Primero afirmo que se llama este método falso, luego afirmo que este método simulado se llama ...", etc. Debería probar el comportamiento del método, no que esté llamando a la secuencia correcta de métodos . Otra cosa: he descubierto que estas pruebas son extremadamente frágiles para la refactorización . Si una prueba es tan quebradiza, se rompe completamente y debe reescribirse cuando se refactorice el código bajo prueba, entonces ¿no se ha perdido uno de los principales beneficios de la prueba unitaria?

No quiero que esta publicación se marque como argumentativa o no como una pregunta. Así que declararé mi pregunta directamente: ¿Cuál es la forma correcta de probar el tipo de código que he descrito, o se entiende que no todo necesita una prueba unitaria ?


Un lado

Solo para tocar su declaración en negrita:

"Debería probar el comportamiento del método, no es que llame a la secuencia correcta de métodos"

El comportamiento del objeto bajo prueba es la secuencia de acciones que toma. Esto es realmente una prueba de "comportamiento", mientras que cuando dices "comportamiento del método", creo que te refieres a las pruebas con estado, como en, dale una entrada y verifica el resultado correcto.

Hago esta distinción porque algunos puristas de BDD van tan lejos como para argumentar que es mucho más significativo probar a qué clase debería recurrir, en lugar de cuáles son las entradas y salidas, porque si sabes completamente cómo se comporta su sistema, entonces tus entradas y salidas serán correctas.

Una respuesta

Aparte de eso, personalmente nunca escribo pruebas exhaustivas para la capa de IU. Si está utilizando un patrón MVVM, MVP o MVC para su aplicación, entonces en un nivel de "1 equipo desarrollador", me resulta insensible y contraproducente hacerlo. Puedo ver los errores en la interfaz de usuario, y sí, comportamientos de burla en este nivel tiende a ser frágil. Estoy mucho más preocupado por asegurarme de que mi dominio subyacente y las capas DAL funcionen correctamente.

Lo que es de valor en el nivel superior es una prueba de integración. ¿Tienes una aplicación web? En lugar de afirmar que sus métodos de control están devolviendo un ActionResult (prueba de poco valor), escriba una prueba de integración que solicite todas las páginas de su aplicación y se asegure de que no haya 404 o 403. Ejecútelo una vez en cada implementación.

Respuesta final

Siempre sigo la regla 80/20 con pruebas unitarias . Para obtener esa última cobertura del 20% en el alto nivel del que está hablando, va a ser el 80% de su esfuerzo. Para mis proyectos personales y la mayoría de mis trabajos, esto no da resultado.

En resumen, estoy de acuerdo. Escribiría pruebas de integración e ignoraría las pruebas unitarias para el código que describes.


Creo que es altamente dependiente del medio ambiente. Si está en un equipo relativamente pequeño y puede mantener la integridad de la prueba, entonces las partes más complejas de su aplicación deben tener pruebas unitarias. Según mi experiencia, mantener la integridad de la prueba en equipos grandes es bastante difícil, ya que las pruebas son inicialmente correctas hasta que inevitablemente se rompen ... en ese punto, o bien se "a fijan" de una manera que niega completamente su utilidad, o ) prontamente comentado.

El punto principal de las pruebas simuladas parece ser que los gerentes pueden afirmar que la métrica de cobertura del código está en% Foo ... ¡así que todo debe estar funcionando! El único caso excepcional en el que posiblemente sean útiles es cuando necesita probar una clase que es un gran dolor recrear de forma auténtica (probar una clase de acción en Struts, por ejemplo).

Soy un gran creyente en escribir pruebas en bruto. Código real, con objetos reales. El código dentro de los métodos cambiará con el tiempo, pero el propósito y, por lo tanto, el comportamiento general, generalmente no.


En general, considero probar este tipo de método / comando para estar listo para el nivel de prueba de integración. Específicamente, hago una "prueba unitaria" para comandos más pequeños y de bajo nivel que (generalmente) no tienen efectos secundarios. Si realmente quiero probar algo que no se ajusta a ese modelo, lo primero que hago es ver si puedo refactorizar / rediseñar para que encaje.

En el nivel de prueba de integración (y / o sistema) más alto, me meto en las pruebas de las cosas que tienen efectos secundarios. Intento burlarme lo menos posible (posiblemente solo recursos externos) en este momento. Un ejemplo sería burlarse de la capa de la base de datos para:

  1. Registre cómo se llamó para obtener datos
  2. Devolver datos almacenados Registrar cómo fue
  3. Registre cómo se llamó para insertar datos manipulados

Según mi experiencia, cuanto menor sea el nivel de tu código (por no ser trivial), mayores serán las pruebas unitarias de valor, en relación con el esfuerzo requerido para escribirlas. A medida que se asciende en la cadena alimentaria, las pruebas se vuelven cada vez más elaboradas y más costosas.

Las pruebas unitarias son fundamentales porque te dicen cuándo rompes algo durante la refactorización.

Las pruebas de nivel superior tienen su propio valor, pero luego ya no se llaman pruebas unitarias; se llaman pruebas de integración y pruebas de aceptación. Las pruebas de integración son necesarias porque le informan qué tan bien funcionan los diferentes componentes de software.

Las pruebas de aceptación son lo que el cliente cierra. Las pruebas de aceptación generalmente las escriben otras personas (no el programador) para proporcionar una perspectiva diferente; los programadores tienden a escribir pruebas de lo que funciona, los probadores intentan romperlo probando lo que no funciona.

La burla solo es útil para pruebas unitarias. Para las pruebas de integración y aceptación, la burla es inútil porque no ejerce los componentes reales del sistema, como la base de datos y la infraestructura de comunicación.


Si utiliza TDD, no debe escribir pruebas después de la implementación, sino al revés. De esta forma, también evitará el problema de hacer que una prueba se ajuste al código escrito. Probablemente tenga que probar ciertas llamadas a métodos dentro de esas unidades, pero no su secuencia (si no es imprescindible para el problema del dominio, proceso comercial).

Y a veces es perfectamente factible no escribir una prueba para un método determinado.