Depura el programa de notas: cinco errores escondidos
Un programa que calcula las notas finales de un módulo compila, se ejecuta y parece funcionar, pero tiene cinco errores. Encuéntralos con el depurador y con casos de prueba bien elegidos: valores límite, el primer y el último elemento, divisiones enteras y copias mal hechas.
- Depurador: puntos de ruptura
- Inspección de variables
- Valores límite
- División entera
- Errores de índice (off-by-one)
- Pruebas de regresión
Enunciado
Un compañero ha escrito el programa que calcula las notas finales de Programación y lo ha dejado para que lo revises antes de publicar las notas. Compila, no lanza excepciones y con su ejemplo de prueba (tres alumnos con notas «normales») el resultado parecía razonable. Pero varios alumnos se han quejado de que su nota no cuadra.
La regla de evaluación es: cada una de las dos prácticas vale un 20 % y el examen un 60 %; si el examen tiene menos de un 4, la nota final no puede pasar de 4 (el examen no se compensa), y la nota se redondea a un decimal. Aprueba quien tiene un 5 o más.
El código del editor tiene exactamente cinco errores, cada uno de una línea. No está permitido reescribir el programa: hay que encontrar cada error y corregirlo. Es lo que se hace en un proyecto real, donde reescribir lo que no se entiende introduce errores nuevos.
Qué tiene que hacer el programa
- La entrada tiene una línea por alumno:
nombre;práctica1;práctica2;examen, con notas de 0 a 10 con punto decimal. - Nota final = 0,2 × práctica1 + 0,2 × práctica2 + 0,6 × examen; si el examen es menor que 4, la nota final es como mucho 4; se redondea a un decimal (las mitades suben).
- Se escribe una línea por alumno, de mayor a menor nota (a igualdad, por nombre): el nombre en 20 caracteres a la izquierda y la nota con un decimal y coma en 5 caracteres a la derecha.
- Al final,
Media: X(la media de las notas finales con un decimal) yAprobados: A de T. - Corrige los cinco errores del programa sin cambiar su estructura. Las pruebas ocultas están pensadas para detectar cada uno.
Entrada
Una línea por alumno: nombre;práctica1;práctica2;examen.
Ejemplos de ejecución
Tu programa debe escribir exactamente esta salida para estas entradas. Las pruebas del editor incluyen estos ejemplos y otros casos ocultos.
Una clase de cinco alumnos
Entrada
Ana Gil;8;9;7.5 Luis Mora;6;5;4 Eva Ruiz;10;10;3.5 Pablo Sanz;5;5;5 Marta Peña;7.5;6.5;6.2
Salida por consola
Ana Gil 7,9 Marta Peña 6,5 Pablo Sanz 5,0 Luis Mora 4,6 Eva Ruiz 4,0 Media: 5,6 Aprobados: 3 de 5
El examen de 4 sí compensa
Entrada
Irene Vidal;9;9;4 Jorge Lara;3;3;3.9
Salida por consola
Irene Vidal 6,0 Jorge Lara 3,5 Media: 4,8 Aprobados: 1 de 2
Guía paso a paso
Intenta resolverlo por tu cuenta y abre un paso solo cuando te atasques: cada uno te acerca a la solución sin dártela entera.
1. Reproduce antes de corregir
Ejecuta las pruebas y mira qué falla y cómo. Compara la salida esperada con la tuya línea a línea: ¿falta un alumno?, ¿las notas tienen decimales?, ¿el recuento de aprobados cuadra? Cada síntoma apunta a una zona del código.
2. Calcula a mano un caso
Elige un alumno del ejemplo y calcula su nota con papel. Si tu cálculo y el del programa no coinciden, el error está en notaFinal. En tu IDE, pon un punto de ruptura dentro de ese método y observa el valor de nota en cada línea.
3. Busca los valores límite
Los errores suelen esconderse en los bordes: un examen de exactamente 4, una nota final de exactamente 5, el primer y el último elemento de una lista. Si una prueba con un 4 en el examen falla, revisa la comparación con 4.
4. Desconfía de las divisiones
En Java, long / int es una división entera: Math.round(nota * 10) / 10 pierde los decimales. Para obtener un decimal hay que dividir entre 10.0.
System.out.println(Math.round(6.66 * 10) / 10); // 6
System.out.println(Math.round(6.66 * 10) / 10.0); // 6.75. Lee el código que «es obvio»
Los errores de copiar y pegar (una variable repetida donde debería haber otra) y los índices que empiezan en 1 en lugar de 0 no los marca el compilador. Léelos despacio, en voz alta si hace falta.
Resuélvelo aquí
El editor trae el esqueleto del programa. Pulsa «Ejecutar» para comprobarlo con los ejemplos y con 2 casos ocultos que buscan los errores típicos.
Ejemplo
Ana Gil;8;9;7.5 Luis Mora;6;5;4 Eva Ruiz;10;10;3.5 Pablo Sanz;5;5;5 Marta Peña;7.5;6.5;6.2
Ana Gil 7,9 Marta Peña 6,5 Pablo Sanz 5,0 Luis Mora 4,6 Eva Ruiz 4,0 Media: 5,6 Aprobados: 3 de 5
Solución explicada
Ver la solución completa
1import java.util.ArrayList;
2import java.util.List;
3import java.util.Locale;
4import java.util.Scanner;
5
6public class Main {
7 record Alumno(String nombre, double nota) {}
8
9 /** Nota final: prácticas al 20 % cada una y examen al 60 %; con menos de un 4 en el examen, como mucho un 4. */
10 static double notaFinal(double practica1, double practica2, double examen) {
11 double nota = 0.2 * practica1 + 0.2 * practica2 + 0.6 * examen;
12 if (examen < 4) nota = Math.min(nota, 4);
13 return Math.round(nota * 10) / 10.0;
14 }
15
16 static String uno(double x) {
17 return String.format(Locale.ROOT, "%.1f", x).replace('.', ',');
18 }
19
20 public static void main(String[] args) {
21 Scanner sc = new Scanner(System.in);
22 List<String> lineas = new ArrayList<>();
23 while (sc.hasNextLine()) {
24 String l = sc.nextLine().trim();
25 if (!l.isEmpty()) lineas.add(l);
26 }
27
28 List<Alumno> alumnos = new ArrayList<>();
29 for (int i = 0; i < lineas.size(); i++) {
30 String[] p = lineas.get(i).split(";");
31 alumnos.add(new Alumno(p[0], notaFinal(Double.parseDouble(p[1]), Double.parseDouble(p[2]), Double.parseDouble(p[3]))));
32 }
33 alumnos.sort((a, b) -> a.nota() != b.nota() ? Double.compare(b.nota(), a.nota()) : a.nombre().compareTo(b.nombre()));
34
35 double suma = 0;
36 int aprobados = 0;
37 for (Alumno a : alumnos) {
38 System.out.printf("%-20s%5s%n", a.nombre(), uno(a.nota()));
39 suma += a.nota();
40 if (a.nota() >= 5) aprobados++;
41 }
42 System.out.println("Media: " + uno(suma / alumnos.size()));
43 System.out.println("Aprobados: " + aprobados + " de " + alumnos.size());
44 }
45}Los cinco errores son clásicos: un índice que empieza en 1 y se salta al primer alumno; un copiar y pegar que suma dos veces la primera práctica; una comparación <= donde iba < (el examen de 4 sí compensa); una división entera que se come los decimales; y un > donde iba >= que deja fuera a quien saca un 5 justo.
Ninguno lo detecta el compilador ni produce una excepción: el programa da resultados plausibles. Solo se ven con casos de prueba que pasan por los bordes (un 4 en el examen, un 5 de nota final, el primer alumno de la lista) o calculando a mano un resultado.
El depurador ahorra tiempo cuando ya sabes dónde mirar: un punto de ruptura en notaFinal y la vista de variables muestran el valor de nota antes y después de cada línea, y ahí aparecen la práctica sumada dos veces y la división entera.
Cada error corregido debería convertirse en una prueba automática (una prueba de regresión), para que nadie lo vuelva a introducir. Es lo que hacen las pruebas ocultas de este ejercicio.
Para ir más allá
- Escribe una prueba JUnit para cada uno de los cinco errores, con el caso mínimo que lo detecta.
- Añade validación: notas fuera de 0-10 o líneas mal formadas deben avisar en lugar de lanzar una excepción.
- Mide la cobertura de tus pruebas con JaCoCo y busca qué ramas quedan sin probar.