El porque de las pruebas unitarias
En la entrada anterior planteaba la necesidad de hacer test unitarios, ademas de explicar como montar la librería compilarla y usarla. Pero quizás hay una pregunta que no respondo y es: para que? Para que una librería cuando podemos hacer programas de ejemplo?
En esta segunda entrada quiero hablar desde un punto de vista diferente y explicar que se busca con las pruebas unitarias y que estrategia tomar para sacarle partido a estas:
Imagina que estas programando y usas las STL. Al ejecutar, aparece un error muy raro. Al debugar, la excepción salta en medio del código de una de las clases que implementan std::vector. Le echaras la culpa a las STL? Las mirarás en profundidad? No. Seguro que no. Tendrás la seguridad de que lo que falla es tu código, por que sabes que las STL han sido probadas una y otra vez (y aun y así todavía tienen errores, aunque son difíciles de encontrar). Esa tranquilidad agiliza el desarrollo puesto que en caso de error siempre miramos el último código. Esa es una de las fortalezas de la reutilización de código.
Cuando pruebas su código, muchas veces haces algunas funciones de prueba pero, lo haces de una manera estructurada? Por ejemplo, esta función:
int dividir(const int a, const int b) { return (a/b); }
Como la probarías?
En principio no haríamos nada raro: un par de llamadas y ver el resultado. Con google test seria así
TEST(funcionDividirTest, dividir) { EXPECT_EQ(dividir(5,2),7); EXPECT_EQ(dividir(5,-5),0); }
No hay nada que hacer. Al final siempre hay bugs. En este caso, por ejemplo, no se contempla la division por cero. Si has hecho bien tu trabajo de pruebas, arreglarás el bug, y añadiras una prueba más a tu set de pruebas, y te asegurarás de que en cada compilación se ejecuten todas las pruebas. La ganancia principal aquí con las pruebas unitarias es que podemos probar nuestro código cada vez que compilamos una nueva version. En codigo más complejo, cuando probamos funciones de más alto nivel, nos permitirá descubrir cuando alguien ha metido la pata.
La vista puesta en la calidad
Resolver bugs es aburrido y una perdida de tiempo. Lo mejor es no fallar al programar. Como es casi imposible, las pruebas unitarias hacen que una vez escritas las pruebas por alli ya no fallará el programa (y si falla sera algo muy localizado). No harías ningun sacrificio por esa buena causa? Pues intenta que el código sea probable. Para hacerlo, intenta que tus funciones y métodos devuelvan algo. Si la función no debería devolver nada, pues que devuelva 0 en caso de éxito y algún valor de error en caso contrario (debidamente documentado). Evidentemente, cambiar el diseño para que sea fácilmente probable puede ser costoso pero creo que se sale ganando con creces.
Se consistente en el manejo de errores. Devuelve un código de error o haz saltar una excepción, pero no mezcles estrategias en el código.
Lleva un control de lo que has probado, pero también de lo que no has probado. Es casi más importante saber que código esta en el aire.
Desacopla tu código, por que si no las inicializaciones son pesadas y penosas. Cada subsistema debería poderse compilar por separado (siempre que sea posible). Desacoplar el código significa que cada subsistema habla con los otros subsistema a través de unas interfaces muy determinadas. Hay que evitar situaciones donde cada clases de un subsistema tiene que conocer muchas cosas de otro subsistema
Test driven development
Pero se puede hacer algo más?Pués si. Imagina que un jefe de proyecto, manager o lo que sea te da esto:
int factorial(const int n) { return 0; }
Y además esto:
TEST(funcFactorial, aceptacion) { EXPECT_EQ(factorial(0), 0); EXPECT_EQ(factorial(1), 1); EXPECT_EQ(factorial(2), 2); EXPECT_EQ(factorial(5), 120); int n = 20; EXPECT_EQ(factorial(n), factorial(n -1)*n); }Digamos que con la interface de la función yo puedo compilar, aunque siempre me devolverá 0, pero yo puedo ir haciendo otro trabajo. Tu trabajo seria programar la funcion de manera que pase el test unitario. Programala como quieras mientras no cambies la signatura de la funcion y siempre que el resultado del test sea verde.
Esta estrategia, de crear lo test antes de el codigo que bajo test, se llama test driven development. Este tipo de estrategia, aunque parezca lenta, puede llegar a reducir el tiempo de desarrollo y aumenta la calidad del codigo final, ademas de reducir el tiempo de desarrollo al haber menos fallos.
entendi perfectamente
ResponderEliminar