Una cosa acerca de las pruebas automatizadas es que requiere que escribas código para que sea comprobable. Esto no es algo malo en sí mismo (de hecho, es bueno porque desalienta muchas prácticas que, por regla general, deben evitarse), pero si está tratando de aplicar pruebas unitarias al código existente, entonces lo más probable es que no lo sea. ha sido escrito de manera comprobable.
Cosas como singletons, métodos estáticos, registros, localizadores de servicios, etc. introducen dependencias que son muy difíciles de burlar. Las violaciones de la Ley de Demeter significan que demasiadas partes de su base de código saben demasiado sobre cómo funcionan otras partes de su base de código, introduciendo dependencias ocultas adicionales que pueden ser difíciles de romper. Todas estas cosas hacen que sea difícil aislar un módulo del resto de la base de código, y si no puede probar sus módulos de forma aislada, las pruebas unitarias pierden mucho de su valor. Si una prueba falla, es debido a una falla en la unidad bajo prueba, o debido a una falla en una de sus dependencias, o tal vez es porque los datos que se extraen a través de una fuente de datos dependiente no es lo que esperaba el escritor de la prueba. ? Si puedes'
La mayoría de las bases de código que he visto que no se crearon teniendo en cuenta las pruebas unitarias tienden a ser inherentemente inestables, ya que los codificadores tienden a centrarse en hacer que el código funcione como esperan, en lugar de hacer el trabajo necesario para mantener el acoplamiento suelto y las dependencias explícitas . El código escrito con las pruebas unitarias en mente tiende a verse muy diferente.
Muchas personas adoptan un enfoque ingenuo para las pruebas unitarias cuando comienzan a hacerlo por primera vez, piensan que pueden escribir una gran cantidad de pruebas para una base de código existente y todo será bueno, pero nunca funciona de esa manera debido a los problemas mencionados anteriormente Comienzan a descubrir que tienen que desordenar las cantidades de configuración en las pruebas unitarias para que se ejecuten, y los resultados a menudo son cuestionables porque la falta de aislamiento en el código significa que no puede rastrear qué causó una falla en la prueba. También tienden a comenzar a intentar escribir pruebas "inteligentes" que demuestren un aspecto muy abstracto de cómo debería funcionar el sistema. Esto tiende a fallar porque una prueba de unidad "inteligente" es una fuente potencial de errores en sí misma. ¿Falló la prueba debido a un error en el módulo probado, o debido a un error en la prueba? Una prueba debe ser tan insoportablemente simple que obviamente no hay posibilidad de que un error pueda estar escondido en ella. De hecho, las mejores pruebas rara vez tienen más de 2 líneas de largo, la primera línea indica a la unidad bajo prueba que haga algo, la segunda afirma que lo que hizo fue lo que se esperaba.
Si su equipo se toma en serio la adopción de pruebas unitarias, no sería prudente comenzar con un proyecto existente. Los proyectos existentes de su equipo probablemente no sean verificables sin una refactorización importante. Es mejor usar un nuevo proyecto como base para aprender sobre las pruebas unitarias, ya que tiene una pizarra limpia para trabajar. Puede diseñar la nueva base de código para favorecer la inyección de dependencias sobre singletons, registros y otras dependencias ocultas, puede escribirla para que dependa de interfaces en lugar de implementaciones, etc. También puede (y debe) escribir las pruebas junto con el código que se está probando, ya que escribir las pruebas después da como resultado pruebas unitarias que aseguran que el módulo probado haga lo que cree que debería estar hecho en lugar de las que prueban que sí lo hace. lo que dicen las especificaciones que debería hacer.
Una vez que haya ganado algo de confianza con las pruebas unitarias, su equipo probablemente comenzará a darse cuenta de las fallas en su código existente que serán obstáculos para las pruebas unitarias. Aquí es cuando puede comenzar a trabajar para refactorizar el código existente para hacerlo más comprobable. No sea ambicioso e intente hacer todo esto de una vez, o intente reemplazar un sistema que funciona con uno completamente nuevo, simplemente comience por encontrar los bits de la base de código que pueden probarse fácilmente (los que no tienen cualquier dependencia o donde las dependencias son obvias) y escriba pruebas para ellas. Sé que dije que escribir una prueba junto con el código es preferible a escribir pruebas después, pero incluso una prueba escrita más tarde todavía tiene valor como punto de partida. Escriba las pruebas como si no supiera nada sobre cómo funciona la clase, aparte de lo que sus especificaciones dicen que debería hacer. Cuando ejecuta las pruebas y obtiene fallas, las especificaciones o la implementación son incorrectas. Verifique ambos para determinar cuál está mal y actualice la prueba o el código en consecuencia.
Una vez que haya recogido la fruta baja, comienza su verdadero trabajo. Debe comenzar a encontrar las dependencias ocultas en su base de código y corregirlas, una a la vez. No se vuelva demasiado ambicioso en este punto, simplemente manténgase haciendo un módulo a la vez, o incluso un solo problema en un módulo, hasta que se solucionen los obstáculos para la prueba y pueda pasar al siguiente bit.
TL: DR: La mayoría de las personas piensan que las pruebas son fáciles y que puedes adaptarlas fácilmente al código existente. Ambos supuestos están equivocados. Si se embarca en un proyecto para obtener pruebas unitarias en sus proyectos con estos dos hechos en mente, es más probable que tenga éxito.