El uso de herramientas de apoyo para la valoración de actividades

Anuncio
El uso de herramientas de apoyo para la valoración de actividades
prácticas de programación
Francisco Durán
Francisco Gutiérrez
Ernesto Pimentel
Ingeniería en Informática
Universidad de Málaga
Resumen
La actual situación de los grupos en los que se ha aplicado la experiencia piloto de
Convergencia al Espacio Europeo de Educación Superior en las titulaciones de Informática de la
Universidad de Málaga, y en particular, en la asignatura “Laboratorio de Tecnología de Objetos”,
hacen difícil un seguimiento personalizado de los alumnos con los recursos de profesorado con los
que se ha contado hasta el momento, que no han diferido de los de cursos anteriores. Más de 200
alumnos se han visto involucrados, y el contenido, fundamentalmente práctico, relativo a temas de
programación, agravan esta situación. Con objeto de conseguir el efecto motivador, el trabajo
continúo y la revisión de temas no asimilados, que un seguimiento personal del profesor suele
producir sobre el alumno, durante este curso se ha hecho un uso sistemático del campus virtual de
la Universidad de Málaga, y éste se ha complementado con la aplicación de una herramienta
desarrollada por los profesores de la asignatura para informar al alumno sobre los errores
detectados en las prácticas desarrolladas, y notificar de la superación de las mismas. Del mismo
modo, se han utilizado herramientas para garantizar la autoría individual de los trabajos. Aunque la
herramienta implementada fue desarrollada inicialmente para ofrecer al profesor una
automatización del proceso de valoración de prácticas, asignando niveles distintos de superación de
objetivos, entre las propiedades más interesantes se puede destacar la emisión de informes al
alumno, indicando las fases superadas en la práctica y la detección inmediata de los errores
cometidos. La inmediatez y detalles de la información facilitada, además de motivar al alumno, le
permiten evolucionar de forma independiente en aquellas situaciones donde no tenga acceso directo
o inmediato al profesor. La herramienta está estructurada de manera que los profesores sólo han de
desarrollar las pruebas concretas de la práctica en cuestión, siendo el motor que pasa las pruebas y
genera los informes común a todas las prácticas.
1. Introducción
La asignatura de “Laboratorio de Tecnología de Objetos” se imparte en la Escuela Técnica Superior de
Ingeniería Informática de Málaga dentro de sus dos especialidades técnicas, Sistemas y Gestión, y en la
ingeniería superior. Dentro del currículo, se sitúa en el segundo cuatrimestre del segundo curso con 6
créditos, con un gran peso de clases prácticas, desarrolladas en su mayoría en laboratorios. Por supuesto,
también tiene un contenido teórico importante necesario para dominar la tecnología, que se introduce por
primera vez en esta asignatura. La necesidad de introducir gran cantidad de conceptos nuevos, hace que se
reduzca el tiempo dedicado al trabajo en el laboratorio.
En su momento, los profesores de la asignatura afrontaron la necesidad de incluir prácticas adicionales a
las tutorizadas en clase de laboratorio, para que los alumnos las desarrollaran como parte de su trabajo
personal (contabilizado en los créditos ECTS), sin presencia del profesor. Si bien los alumnos se felicitaron
de esta idea, la realización de las mismas resultaron algo frustrante para ellos porque cierto tipo de
1
dificultades les obligaba a consultar al profesor antes de proseguir con el trabajo, impidiendo la continuidad
del mismo. Observamos que muchos de los estudiantes realizaban las prácticas en días no lectivos (fines de
semana, festivos, Semana Santa, etc.), por lo que esa consulta no tenía lugar hasta la reanudación del período
lectivo. Esto frustraba enormemente al alumno, frenando su iniciativa y por tanto su aprendizaje.
Con objeto de resolver en parte ese problema, decidimos introducir una herramienta de apoyo al
aprendizaje del alumno, de manera que cuando se producía esa parada, pudiera informarle de la situación
anómala en la que se encontraba, es decir de los fallos que estaba cometiendo y que le impedían seguir con la
práctica.
Además, era necesario que la herramienta fuera capaz de ofrecer una valoración justificada al alumno del
trabajo realizado en la práctica de manera que pudiera ir mejorando esa valoración atendiendo a las
indicaciones que la herramienta proporciona.
De este modo, uno de los objetivos iniciales para la construcción de la herramienta, que era el soporte
para la evaluación automatizada de ejercicios, se vio superado por las posibilidades pedagógicas que la
herramienta ofrecía cuando el alumno hacía uso de ella. Así nació JCap, una herramienta para el apoyo a la
corrección automática de prácticas.
2. Antecedentes
La asignatura de Laboratorio de Tecnología de Objetos, impartida en las tres titulaciones de Informática
de la Universidad de Málaga, dispone de un temario común y se encuentra fuertemente coordinada por los
profesores que la imparten. Las prácticas de la asignatura se realizan utilizando como lenguaje de
programación Java.
En la actualidad existen varias herramientas dedicadas a verificar la corrección de programas [1],
orientadas, por lo tanto, al desarrollo de sistemas software. Entre éstas podemos destacar JUnit [2], centrada
en el lenguaje Java, y adecuada por proporcionar una comprobación de la corrección de cada aplicación que
se desarrolla.
Para usar este tipo de herramientas, el programador debe generar lo que se denominan clases de prueba,
una por cada clase que necesite verificar. Cada uno de estos elementos debe verificar la corrección de todos
los métodos (funciones) que incluye la clase (módulo en un lenguaje orientado a objetos). Así, se genera una
batería de pruebas que puede lanzarse de forma automática cada vez que se produce un cambio en la
implementación que se está desarrollando.
Cuando una de esas pruebas encuentra un problema, se informa al usuario mostrando el punto donde se
ha producido y el motivo que lo ha provocado. Esto no detiene al resto de pruebas que siguen ejecutándose
proporcionando al final un informe con los resultados obtenidos en cada prueba.
2
Sobre JUnit se han desarrollado otras herramientas que ofrecen una interfaz más amigable de manera que
el usuario va siguiendo la evolución de las pruebas que la herramienta va ejecutando sobre sus clases.
En el ámbito de la docencia, una herramienta como JUnit también puede resultar de ayuda tanto para el
alumno como para el profesor. Para el alumno, porque unas clases de prueba le ayudan a comprobar
que cualquier modificación o implementación realizada satisface las especificaciones. Además, si es
el profesor el que proporciona las clases de prueba el alumno se siente corroborado en su trabajo.
Para el profesor, porque puede obtener información sobre la funcionalidad de los ejercicios
entregados por sus alumnos. Sin embargo, en este caso, el profesor debe repetir el procedimiento con
cada uno de los alumnos.
Dado que la herramienta tiene un enfoque claramente orientado a la Ingeniería del Software, se
encuentran ciertas dificultades cuando se intenta utilizar en el ámbito educativo. Enumeramos a continuación
algunas de estas dificultades:
•
JUnit es adecuado para ejecutar las pruebas sobre un conjunto de clases, pero es el profesor el que
debe hacer esto con cada alumno. Evidentemente, éste es un problema menor, ya que se podría
resolver diseñando una herramienta sobre JUnit que automatice el proceso.
•
La integración de JUnit con las clases a verificar se hace sobre el código fuente. Esto es, las clases de
prueba que JUnit define se compilan junto con las clases que se quieren verificar. Esto significa que,
si por cualquier razón, las clases a verificar no compilan, no es posible ejecutar la prueba.
Nuevamente este problema es evitable pues también en JUnit podemos desarrollar herramientas para
utilizar simplemente los compilados de las clases de prueba. Sin embargo, en ese caso, la
información sobre si las clases del alumno compilan o no, estarían fuera de la prueba en sí, y no
podrían ser tenidas en cuenta en el informe emitido y la valoración final.
•
Si un método produce ejecuciones que no terminan (error típico y habitual en alumnos de este nivel),
JUnit no se detiene, ni advierte esta situación, por lo que necesitaría de la intervención del profesor.
Si lo hace, se detiene la ejecución de todas las pruebas, pero en el ambiente docente puede que esta
circunstancia aparezca con frecuencia como consecuencia de un mal diseño. Evidentemente se debe
advertir de este problema al alumno, pero el resto de pruebas no debería detenerse por ello. Aún no
entrando en ejecuciones que no terminan, es deseable fijar tiempos máximos para realizar las
pruebas de manera que si se supera ese tiempo en alguna prueba se interrumpa su ejecución,
informando nuevamente de que el algoritmo utilizado sobrepasa los requisitos temporales
considerados.
•
JUnit no es capaz de valorar el grado de cumplimiento de las especificaciones. Es decir,
simplemente informa de que algo ha ido mal pero no valora el que haya ido bien. En el contexto de
3
una actividad docente es de sumo interés valorar el trabajo del alumno atendiendo a cómo ha
cumplido los objetivos y, si es necesario, penalizando las situaciones anómalas que aparezcan:
ejecuciones que no terminan, provocar excepciones no esperadas, problemas de compilación, etc.
Como ya se ha mencionado, algunos de los problemas identificados se pueden resolver con cierta facilidad
utilizando JUnit. No obstante, otros, quizás los más interesantes desde el punto de vista docente, no tienen
fácil solución. En este sentido sería deseable disponer de una herramienta como JUnit pero que resuelva esas
carencias.
Así, nace JCap, una herramienta para la corrección automática de prácticas. JCap resuelve todos los
problemas mencionados anteriormente, y en particular, valora el trabajo realizado por cada alumno
permitiendo tener en cuenta malas prácticas de diseño. Al igual que JUnit, JCap no libera al profesor de la
tarea de confeccionar las clases de prueba.
3. JCap
Para cada práctica JCap permite realizar una batería de pruebas. Cada prueba contendrá una secuencia de
comprobaciones. Si en una prueba falla una comprobación, la prueba termina y se procede con la siguiente.
Al final del proceso se tendrá una valoración de cada prueba, un informe de los errores cometidos y una
evaluación final que será la suma de estas valoraciones. Las valoraciones pueden ponderarse para que unas
pruebas tengan más peso (o importancia) que otras en la valoración final.
Cada prueba deberá implementar la interfaz Caso que obliga a definir los métodos float valor() y void
evaluar(Aserto). El primero proporciona la valoración máxima para esta prueba y el segundo desarrolla la
prueba en sí, e incluirá las diferentes comprobaciones que la conforman.
La valoración de una prueba se obtiene según se superen la secuencia de comprobaciones que llamaremos
niveles. Cada prueba dispone de un nivel de comienzo (normalmente 0) y un nivel máximo. Según se
superen comprobaciones, se van consolidando niveles hasta alcanzar el máximo. La valoración máxima se
obtiene cuando el nivel máximo es alcanzado.
Pasamos a definir algunos conceptos alrededor de una prueba:
Valor de la prueba:- Será la valoración máxima que puede alcanzarse en esta prueba. Cada prueba puede
tener un valor diferente lo que permite ponderar las diferentes pruebas. Por ejemplo, es posible realizar dos
pruebas, una con valor 8 y otro con valor 10. Si el alumno obtiene un 5 y un 7 respectivamente, las
valoraciones de cada prueba serán 5/8 y 7/10. La valoración final será 5/8+7/10. No existe un valor de la
prueba por defecto por lo que debe implementarse el método float valor() que devuelve ese valor.
Nivel de comienzo consolidado:- Será el nivel consolidado inicial en esta prueba. Éste será el nivel
consolidado si no se supera la primera comprobación. Por defecto este valor es 0 pero puede modificarse.
4
Nivel próximo a consolidar:- Será el próximo nivel que se consolidará si se supera una comprobación (o
grupo de comprobaciones). Su valor comienza por defecto en 1 aunque puede modificarse.
Nivel consolidado:- Cada vez que se supera una comprobación (o un grupo de comprobaciones) se consolida
el nivel indicado en el nivel próximo mencionado anteriormente y se incrementa. El nivel consolidado será el
mayor de todos los niveles consolidados hasta ese momento.
Nivel máximo a consolidar:- Será un máximo para el nivel consolidado. No será posible consolidar un nivel
superior a este valor.
El cálculo de la valoración de una prueba se calcula como el cociente entre el nivel consolidado de la prueba
y su nivel máximo, multiplicado por el valor de la prueba. Esta valoración puede verse reducida por alguna
de las siguientes situaciones:
•
La prueba no termina por entrar en un bucle infinito o retardar demasiado su conclusión.
•
La prueba termina produciendo una excepción inesperada.
•
La prueba termina por un problema de compilación.
Las reducciones en la valoración se definen en tantos por 1 sobre el valor de la prueba y pueden: a) tomarse
por defecto, b) definir unos valores específicos para todas las pruebas o c) definir unos valores particulares
para una prueba en concreto.
La herramienta está dotada de un mecanismo de control que impide que el nivel máximo, próximo a
consolidar y de comienzo consolidado sean incoherentes. Si por alguna circunstancia esto se produce, se
lanzará una excepción del tipo AsertoError con información del problema detectado, terminando
abruptamente la corrección de la práctica. Este error indica que la batería de pruebas no se ha diseñado
correctamente, por lo que habrá que revisar los valores iniciales de cada prueba y las pruebas en sí y ejecutar
la corrección de nuevo. Es decir, se trata de un error cometido por el profesor o asistente que elaboró la
prueba, y no del alumno.
Como ya se ha mencionado, la ejecución de una prueba se realiza dentro del método void evaluar(Aserto). El
argumento es un objeto que permite inicializar la prueba, modificar sus parámetros, ejecutar las
comprobaciones incluidas en esa prueba y finalizarla.
Antes de empezar con una prueba debe inicializarse, debiéndose indicar el nivel máximo para la prueba.
También es posible especificar el nivel próximo y, si es necesario, el nivel de comienzo consolidado.
Una vez inicializada, el marco de trabajo permite realizar comprobaciones utilizando los siguientes métodos
o funciones:
void verdad(boolean, String),
5
void falso(boolean, String),
void verdadMismoNivel(boolean, String),
void falsoMismoNivel(boolean, String),
void verdadEnNivel(boolean, int, String),
void falsoEnNivel(boolean, int, String).
El último argumento es una cadena de caracteres que describe el motivo por lo que la comprobación
(proporcionada como primer argumento) puede no satisfacerse. Esta cadena será mostrada en el informe que
se genera si, efectivamente, la comprobación falla. Los dos primeros métodos consolidan el nivel próximo a
consolidar y lo incrementa. Los dos siguientes aparecen dentro de un grupo de comprobaciones y se realizan
en un mismo nivel, consolidándose éste si todas las comprobaciones de este tipo se satisfacen. Cualquier
aparición de otro tipo de comprobación, consolidará el nivel. Los dos últimos métodos permiten realizar
comprobaciones en un nivel específico (proporcionado como segundo argumento). Para finalizar la prueba se
utiliza el método void finalizar() que finaliza la prueba habiendo superando todas las comprobaciones,
verificando, por tanto, que se ha alcanzado el nivel máximo, y lanzando una excepción AsertoError si esto
no ocurre así.
Existen otras características de inicialización y finalización que permiten mayor flexibilidad a la herramienta.
4. Paquete jcap.jar
En esta sección, describimos de forma técnica las componentes que conforman JCap. Para el lector que no
esté interesado en la estructura interna de la implementación, puede omitir la lectura de la misma. En la
6
Figura 1: jcap.jar
figura 1 se muestra el diagrama UML (Unified Modeling Language) del paquete jcap.jar para dar una idea
global de las clases, intefaces y métodos que la herramienta contiene:
•
Aserto: Los objetos de esta clase son los encargados de inicializar la prueba, realizar las distintas
comprobaciones que forman parte de la misma y finalizarla. También permite parametrizar la prueba
y mantiene la información necesaria para poder valorarla.
•
AsertoError: Esta clase se encarga de gestionar las excepciones que puede lanzar la aplicación por
un mal diseño de alguna de las pruebas.
•
Corrector: Esta clase es la encargada de lanzar la batería de pruebas que forman la corrección e ir
generando el informe. También genera una calificación global de la práctica a tenor de los resultados
obtenidos en cada prueba.
•
Caso: Es una interfaz que establece los métodos que necesita implementar una prueba. Básicamente
se trata del método que lanza la prueba y la valoración de la misma.
•
CorrectorConstantes: Esta interfaz define varias constantes usadas por la clase Corrector.
Valoración por defecto de una prueba, tiempo máximo de espera por prueba, disminución en la
valoración en caso de no terminación, excepciones incontroladas o inadecuadas, errores, etc.
4.1. Estructura básica de una corrección con JCap
En la valoración de una práctica se debe generar una clase de prueba por cada prueba que queramos
realizar sobre la misma. Por ejemplo: PruebaBasicaVertices, PruebaBasicaArcos, pueden ser dos pruebas,
sobre una práctica relacionada con la implementación de grafos. La primera verifica el correcto
funcionamiento de la inserción y extracción de vértices en el grafo mientras que la segunda verifica la
correcta inserción y extracción de arcos en el grafo. Cada prueba debe implementar la interfaz Caso del
paquete jcap.
Una táctica a emplear a la hora de realizar las clases de prueba es hacer que todas hereden de una clase
abstracta que implemente la interfaz Caso. En esa clase abstracta podemos definir cualquier constante común
a todos las pruebas (número de repeticiones de un bucle, valores por defecto, nombres de ficheros, etc...),
incluso una valoración de la prueba común por defecto. Por ejemplo:
import jcap.Caso;
abstract public class DriverGrafos implements Caso {
public static final int NUMVERTICES = 30;
public static final int NUMARCOS = NUMVERTICES * NUMVERTICES;
public static final int REPETICIONES = 7;
public float valor() {
return 10f;
}
}
7
import jcap.Aserto;
class PruebaBasicaVertices extends DriverGrafos {
public void evaluar(Aserto aserto) {
// Aquí se realizan las comprobaciones
…
}
}
Será responsabilidad del profesor o ayudante definir una aplicación que genere un objeto de la clase
Corrector para agrupar todas las clases de prueba. Cuando se invoque el método void ejecutar() se ejecutarán
todas las pruebas generando un informe en el dispositivo de salida estándar (pantalla). Por ejemplo:
import jcap.Caso;
import jcap.Corrector;
public class MainTestGrafos {
public static void main(String[] args) {
Caso[] pruebas = {
new PruebaBasicaVertices(),
new PruebaBasicaArcos()
};
Corrector test =
new Corrector(pruebas, 9000, 0.2f, 0f, 0.2f, 10f);
test.ejecutar();
}
}
Los argumentos del constructor de la clase Corrector son: el array con los objetos que implementan las
pruebas; el tiempo máximo que se dedicará a cada prueba en milisegundos; los tres siguientes argumentos
son penalizaciones dadas en tanto por 1: por no terminar, por lanzar una excepción no esperada y por
provocar un error. El último argumento es la máxima calificación posible para la corrección.
4.2. Creación de casos de prueba
Ya hemos mencionado que para crear una prueba se ha de implementar la interfaz Caso. Esta interfaz
obliga a definir los métodos void evaluar(Aserto aserto) y float valor(). Este último método se usa para
indicar la calificación máxima de ese caso de prueba lo que permite ponderar el peso de cada caso. Dentro
del método void evaluar(Aserto aserto) se realizan las comprobaciones que forman parte de la prueba. Un
ejemplo para comprobar la corrección de la inserción y la extracción de vértices en un grafo podría ser el
siguiente:
import jcap.Aserto;
import grafos.*;
class PruebaBasicaVertices extends DriverGrafos {
public void evaluar(Aserto aserto) {
aserto.inicializar(8);
Grafo<String> g = new GrafoLAdj<String>();
aserto.verdad( (g.tamaño() == 0) ,
"Un GrafoLAdj no tiene 0 vértices tras crearse");
for (int i = 1; i <= REPETICIONES; i++) {
g.ins("1");
aserto.verdadMismoNivel( (g.tamaño() == 1),
"Tras insertar el primer vértice
8
debería haber un vértice");
aserto.verdadMismoNivel((g.pertenece("1")),
"Tras insertar el primer vértice
este no está");
g.elim("1");
aserto.verdadMismoNivel( (g.tamaño() == 0),
"Tras eliminar el único vértice
el tamaño no es 0");
g.ins("1");
aserto.verdadMismoNivel( (g.tamaño() == 1) ,
"No tiene un vértice tras insertar
uno en un grafo vacío (posiblemente
no quedó en un estado correcto tras
la extracción previa)");
aserto.verdadMismoNivel( (g.pertenece("1")),
"No está el vértice insertado
en un grafo vacío (posiblemente
no quedó en un estado correcto tras
la extracción previa)");
g.ins("1");
aserto.verdadMismoNivel((g.tamaño() == 1),
"Cambia el tamaño del grafo
tras insertar en él un vértice
que ya estaba");
aserto.verdadMismoNivel( (g.pertenece("1")),
"Tras insertar un vértice que ya
estaba dice que éste no está
en el grafo");
aserto.separarMismoNivel();
}
aserto.finalizar();
}
public float valor(){
return 10f;
}
}
Obsérvese que la expresión aserto.separarMismoNivel() separa grupos de comprobaciones de un
nivel a otro, consolidando el anterior.
5. Perspectivas del profesor y el alumno sobre JCap
Una vez diseñada la prueba, la herramienta ofrece dos perspectivas para su ejecución. Una para el profesor y
otra para el alumno. La perspectiva del profesor permite la ejecución de una prueba sobre un conjunto de
prácticas del mismo o diferentes alumnos, generando un informe por cada alumno, y un resumen global con
las valoraciones de cada una de las prácticas. La perspectiva que la herramienta ofrece al alumno permite
seleccionar el programa de pruebas que desea aplicar a su práctica, permitiendo la selección de pruebas
individuales, con objeto de poder probar funcionalidades parcialmente desarrolladas. En ambas perspectivas,
los informes pueden ser generados con distinto nivel de detalle, atendiendo a los intereses del usuario
(profesor o alumno):
9
•
Calificación: Sólo se proporciona información sobre los errores y valoraciones de las pruebas.
•
Comentario: Además se detallan las pruebas que se van realizando.
•
Instrucción (sólo accesible para el profesor): Además de la información proporcionada por los dos
niveles anteriores, se detallan las operaciones que la herramienta va ejecutando sobre la práctica a
valorar.
6. Conclusiones
En este trabajo se ha descrito una herramienta para la corrección y valoración de prácticas de laboratorio en
asignaturas de programación. Aunque está orientada al lenguaje de programación Java, su adecuación a
cualquier otro lenguaje orientado a objetos sería bastante directa. La herramienta constituye un buen soporte
al profesor cuando éste tiene grupos numerosos que impiden un adecuado seguimiento de las prácticas. Al
mismo tiempo, permiten que el alumno pueda desarrollar su trabajo con cierta independencia, obteniendo de
la herramienta una respuesta rápida sobre los posibles fallos que pueda cometer, así como una valoración
sobre el nivel del cumplimiento de objetivos. Esto suele redundar positivamente en la motivación del
alumno, tanto por la “tutorización virtual” recibida, como por la inmediatez de la misma.
Como trabajo futuro, planteamos la integración de JCap con Moodle [3] (herramienta de campus virtual
utilizada en la Universidad de Málaga), de forma que la ejecución de una prueba se pueda establecer como
tarea de Moodle, y la valoración generada por la herramienta se incorpore al expediente del alumno en el
campus.
Referencias
[1] Paul Hamill. Unit Test Frameworks. O’Really. 2004.
[2] Andy Hunt y Dave Thomas. Pragmatic Unit Testing. 2003.
[3] Moodle: Course Management System. http://www.moodle.org.
10
Descargar