Cómo encarar un TP
Table of Contents
Cómo encarar un TP #
Síntesis #
Lo que quiero mostrar en este post es cómo hacer que tu tiempo de programación te rinda, básicamente puntualizando lo que tenés que hacer, realizando pequeños cambios en el código por vez y probar estos cambios.
Puntualizar las tareas a hacer #
Voy a empezar con una anécdota sobre lo que me pasó cursando la materia Sistemas Operativos. En una clase nos encomendaron una tarea y básicamente a la clase siguiente entregué un pequeño programa y un detallado informe de cómo funcionaba, pero pasó que no era exactamente lo que yo había hecho lo que se pedía, simplemente había empezado con lo pedido, dejé volar mi imaginación y terminó como terminó. El primer error fue no anotar, pormenorizar y atenerme al pedido.
Para ejemplificar vamos a tomar el TP1 del primer cuatrimestre de la materia Algortimos 2 para Electrónicos (Faculta de Ingeniería, UBA):
-
Línea de comandos
-
implementar opción -i que indique el archivo/stream de entrada (default: stdin) implementar opción -o que indique el archivo/stream de salida (default: stdout) pasando como parámetro “-” indica stdin o stdout el orden de las opciones es irrelevante, pueden venir en cualquier orden
Valor de retorno
nulo en caso de terminar bien no nulo en caso de terminar mal
Salida
Si termina mal debe mostrar “error: “ má una descripción del mismo Si termina bien consta de una secuencia de líneas con información del archivo de entrada (ver punto 5.2 del enunciado)
Entrada
No puede haber líneas vacías Cada línea consta de 1 comando y 1 o 2 parámetros (ver punto 5.1 para formato de cada comando)
En realidad hay algunas cosas más que podrían anotarse, pero para muestra nos alcanza esto. Notar que esto es sólo una lista de tareas a realizar pero los detalles específicos (formatos, etc. ) no son colocados para 1) poder tener en un vistazo de qué hicimos y cuánto falta por hacer y 2) no cometer errores de transcripción al copiarlo del enunciado a nuestra lista
Cómo probar los cambios #
Un tiempo después de cursar Sistema Operativos cursé Matemática Discreta y tenía un TP que funcionaba pero había que arreglar 2 o 3 cosas, así que en la devolución de la segunda entrega para mi sorpresa algo que funcionaba había dejado de funcionar y tuve que invertir aún más tiempo en hacer algo que ya había hecho, pero que se había roto.
En principio cada vez que se codifica normalmente se prueba invocando el programa, se determina que funciona y se continúa con la siguiente parte del código. En principio todo funciona, pero este plan tiene un par de fallas 1) qué pasa si más adelante esto deja de funcionar? cómo darse cuenta del problema? y 2) por qué código de más, previo a ejercitar el código que se quiere probar, sólo para saber que una parte de este sigue funcionando?
Para estos dos puntos la respuesta, en principio, es testing y programación orientada a objetos. En honor a la verdad no es absolutamente necesario programar con orientación a objetos pero lo hace muy conveniente a la hora de hacer testing ya que al dividir las distintas partes del código en unidades autosuficientes (o algo similar) permite poder probar sólo el código necesario.
Para empezar hay que hacer que en “Cada línea consta de 1 comando y 1 o 2 parámetros” así es que se hace una clase que reciba una línea y diga si no responde a esta estructura y en caso de responder cuál es el comando, cuáles sus parámetros. Para esto se propone la siguiente clase:
class LineParser {
private:
string line;
string command;
vector *params;
public:
LineParser(const string );
LineParser(const LineParser & );
virtual ~LineParser();
void parse();
string getCommand() const;
vector * getParams() const;
};
y abstrayéndonos de su implementación vamos a probar que funcione como necesitamos. En principio se puede probar:
- pasar una línea con 1 comando y 1 argumento pasar una línea con 1 comando y 2 argumentos
y probar que en cada una de estas me devuelva el resultado correcto (en el primero que me devuelva 1 argumento y en el segundo que me devuelva 2 argumentos). A cada uno de estas pruebas se las llama casos de prueba.
Para implementar estos casos de prueba vamos a usar una lib especializada para hacer testing que se denomina cpptest. En esta cada caso de prueba se implementa como un método de una clase codificada en C++ y estos métodos se agrupan en una clase denominada test suite.
Pero mejor veamos un ejemplo :
void LineParserTestSuite::comandoParametro() {
LineParser lp(“AAAA bbbbb”);
lp.parse();
TEST_ASSERT_EQUALS(“AAAA”, lp.getCommand());
vector *p = lp.getParams();
TEST_ASSERT_EQUALS(1, p->size());
TEST_ASSERT_EQUALS(“bbbbb”, (*p)[0]);
}
void LineParserTestSuite::comando2Parametros() {
LineParser lp(“AAAA bbbbb ccc”);
lp.parse();
TEST_ASSERT_EQUALS(“AAAA”, lp.getCommand());
vector *p = lp.getParams();
TEST_ASSERT_EQUALS(2, p->size());
TEST_ASSERT_EQUALS(“bbbbb”, (*p)[0]);
TEST_ASSERT_EQUALS(“ccc”, (*p)[1]);
}
Como se puede ver básicamente se trata de crear el objeto correspondiente, llamar a los métodos correspondientes en el orden adecuado (en este caso el método parse() ), obtener el resultado ( getParams() ) y verificar que el valor obtenido sea el esperado. Este último paso se hace a través de un assert (aserción) y que si el resultado no es el esperado entonces se nos lo hace saber a través de un error.
Comentarios finales #
Este post es sólo una visión muy por arriba y probablemente hay un millón de dudas sobre cómo realmente hacer para probar y demás, así que para ver este ejemplo funcionando ejecutar :
git clone https://gitlab.com/bit-man/input-generator-a2.git && \
cd tp-sample/src/test/ && \
make clean test && ./tests.exe
...
LineParserTestSuite: 7/7, 100% correct in 0.000109 seconds
Total: 7 tests, 100% correct in 0.000109 seconds
y como se puede ver en las líneas finales se ejecutaron 7 tests de 7 y todos terminaron bien. El código de LineParser se puede verlo en el directorio src y el código de test en src/test.