Testing contra Criterios de Aceptación y Definition of Done
Valentina terminó la historia "reservar un espacio" de EventoUAB, la probó una vez y funcionó. ¿Está terminada? Esta guía parte de los criterios de aceptación que ya escribiste, los convierte en casos de prueba normales, inválidos y de borde, y fija la lista de chequeo del equipo —la Definition of Done— que decide cuándo una historia se puede cerrar.
Bienvenido
Es viernes del Sprint 1 en EventoUAB. Valentina abre el calendario, reserva el auditorio de 14:00 a 16:00 y la reserva aparece en "mis reservas". Dice: "Listo, lo probé y anda." Mateo, que está por revisar su Pull Request, le hace una sola pregunta: "¿Probaste qué pasa con exactamente 4 horas?" Valentina no lo probó.
Esa pregunta es el tema de la guía. Probar que algo funciona una vez no es lo mismo que comprobar que cumple lo que prometía la historia. Lo primero lo hace cualquiera con un clic; lo segundo requiere partir de los criterios de aceptación y recorrerlos con intención.
Al finalizar esta guía sabrás convertir cada criterio de aceptación en casos de prueba normales, inválidos y de borde, escribirlos como pruebas automáticas en Node.js, entender por qué los casos de borde atrapan errores que los demás dejan pasar, y distinguir los criterios de aceptación de una historia de la Definition of Done de todo el equipo.
Encontrarás actividades cortas y autocorregibles —selección múltiple, clasificación, emparejamiento y completar espacios— repartidas a lo largo de la guía, y una sección de ejercicios de razonamiento donde se te da un escenario y debés decidir antes de comprobar.
🎯 Del criterio al caso
Cada "Dado / Cuando / Entonces" ya es, casi literal, una prueba.
🧪 Tres familias
Normal, inválido y de borde: qué cubre cada una y cuál suele olvidarse.
✅ Definition of Done
La lista que el equipo acuerda una vez y aplica a todas las historias.
Del criterio a la prueba
En la guía de historias de usuario escribiste criterios con el formato Dado / Cuando / Entonces. Ese formato no es casualidad: tiene la misma forma que una prueba automática, que siempre hace tres cosas en orden.
| En el criterio | En la prueba | Ejemplo en EventoUAB |
|---|---|---|
| Dado que... | Preparar: dejar el sistema en el estado inicial. | El auditorio está libre de 14:00 a 16:00. |
| Cuando... | Ejecutar: llamar a la función con la acción. | El club pide reservar de 14:00 a 16:00. |
| Entonces... | Comprobar: afirmar el resultado esperado. | La reserva queda registrada y el espacio ocupado. |
Los criterios dicen qué debe cumplirse. Para comprobarlo hace falta pensar con qué datos: un criterio como "una reserva dura como máximo 4 horas" se prueba con más de un valor, y los valores que importan son los que están justo en el límite.
Une cada parte del criterio con lo que hace en una prueba.
Normal, inválido y de borde
Para cada criterio conviene pensar tres familias de casos. Un equipo apurado suele escribir solo la primera; la mayoría de los errores que llegan a producción viven en las otras dos.
| Familia | Qué prueba | En "reservar un espacio" |
|---|---|---|
| Normal | El camino feliz: datos correctos, todo sale bien. | Reservar el auditorio de 14:00 a 16:00 estando libre. |
| Inválido | Datos o situaciones que el sistema debe rechazar con un error claro. | Reservar un horario ya ocupado; fin anterior al inicio. |
| Borde | Valores justo en el límite de una regla, donde cambia la respuesta. | Exactamente 4 horas; empezar justo cuando termina otra reserva. |
Buscá en cada criterio un número, un límite o una comparación ("máximo", "al menos", "antes de", "no se solapan"). Probá el valor exacto, uno apenas por debajo y uno apenas por encima. Si el criterio no tiene ningún límite, probablemente tampoco tenga casos de borde — y está bien.
La regla es "una reserva no puede durar más de 4 horas ni solaparse con otra". ¿A qué familia pertenece cada caso de prueba?
Las pruebas en código
Probar a mano cada caso en cada cambio no escala. Una prueba automática guarda el caso como código y lo vuelve a correr en segundos. Esta es la función que escribió Valentina, con las tres reglas de la historia: fin posterior al inicio, máximo 4 horas y sin solapamiento.
const DURACION_MAX_HORAS = 4;
export function reservar(reservas, espacioId, inicio, fin) {
if (fin <= inicio) {
throw new Error("La hora de fin debe ser posterior al inicio");
}
const horas = (fin - inicio) / 3600000;
if (horas > DURACION_MAX_HORAS) {
throw new Error("Máximo 4 horas por reserva");
}
const choca = reservas.some(
(r) => r.espacioId === espacioId && r.inicio < fin && r.fin > inicio
);
if (choca) {
throw new Error("Espacio ocupado en ese horario");
}
const nueva = { espacioId, inicio, fin };
reservas.push(nueva);
return nueva;
}
Y estas son las pruebas, una por cada caso que pensamos arriba. Cada una sigue el mismo orden: preparar, ejecutar, comprobar. Usan el corredor de pruebas que ya trae Node (node:test), sin instalar nada.
import { test } from "node:test";
import assert from "node:assert/strict";
import { reservar } from "./reservas.js";
const h = (hora, min = 0) => new Date(2026, 9, 20, hora, min);
test("normal: espacio libre, la reserva queda registrada", () => {
const reservas = []; // Dado
reservar(reservas, "auditorio", h(14), h(16)); // Cuando
assert.equal(reservas.length, 1); // Entonces
});
test("inválido: horario ya ocupado, se rechaza y no se duplica", () => {
const reservas = [{ espacioId: "auditorio", inicio: h(14), fin: h(16) }];
assert.throws(() => reservar(reservas, "auditorio", h(15), h(17)), /ocupado/);
assert.equal(reservas.length, 1);
});
test("inválido: fin anterior al inicio", () => {
assert.throws(() => reservar([], "auditorio", h(16), h(14)), /posterior/);
});
test("borde: exactamente 4 horas se acepta", () => {
const reservas = [];
reservar(reservas, "auditorio", h(10), h(14));
assert.equal(reservas.length, 1);
});
test("borde: 4 horas y 1 minuto se rechaza", () => {
assert.throws(() => reservar([], "auditorio", h(10), h(14, 1)), /Máximo/);
});
test("borde: empieza justo cuando termina otra, no es choque", () => {
const reservas = [{ espacioId: "auditorio", inicio: h(14), fin: h(16) }];
reservar(reservas, "auditorio", h(16), h(18));
assert.equal(reservas.length, 2);
});
test("borde: inicio y fin iguales se rechaza", () => {
assert.throws(() => reservar([], "auditorio", h(14), h(14)), /posterior/);
});
✔ normal: espacio libre, la reserva queda registrada
✔ inválido: horario ya ocupado, se rechaza y no se duplica
✔ inválido: fin anterior al inicio
✔ borde: exactamente 4 horas se acepta
✔ borde: 4 horas y 1 minuto se rechaza
✔ borde: empieza justo cuando termina otra, no es choque
✔ borde: inicio y fin iguales se rechaza
ℹ tests 7 ℹ pass 7 ℹ fail 0
Fijate que ninguna prueba menciona clases ni detalles internos: todas hablan del comportamiento que prometía la historia. Si mañana el equipo reescribe reservar por dentro, las mismas pruebas siguen sirviendo — y avisan si el comportamiento cambió.
Valentina probó "reservar" una sola vez, con un espacio libre, y salió bien. Mirando los criterios de la historia, ¿qué le falta cubrir?
Criterio: "una reserva dura como máximo 4 horas". Armá el caso de borde para ese límite.
Dado un espacio , cuando el club pide una reserva de , entonces el sistema .
Por qué importa el borde
Una prueba que pasa te dice algo concreto: ese caso funciona. No te dice que todo funciona. Para ver por qué los casos de borde pesan tanto, hagamos un experimento: introducir a propósito un error chico y ver qué prueba lo detecta.
Un cambio de una sola línea en reservar, del tipo que se cuela en un apuro o lo sugiere una IA sin que nadie lo note: horas > DURACION_MAX_HORAS pasa a horas >= DURACION_MAX_HORAS. Parece inocente, pero ahora una reserva de exactamente 4 horas se rechaza.
| Prueba | Con el error |
|---|---|
| normal: espacio libre, la reserva queda registrada | ✔ pasa |
| inválido: horario ya ocupado | ✔ pasa |
| inválido: fin anterior al inicio | ✔ pasa |
| borde: exactamente 4 horas se acepta | ✖ falla |
| borde: 4 horas y 1 minuto se rechaza | ✔ pasa |
| borde: empieza justo cuando termina otra | ✔ pasa |
| borde: inicio y fin iguales | ✔ pasa |
Resultado real de correr las siete pruebas contra el código modificado: seis siguen en verde y solo el borde exacto de 4 horas detecta el error. Con un cambio parecido en la regla de solapamiento (r.fin > inicio por r.fin >= inicio), el único que falla es el caso "empieza justo cuando termina otra".
Un equipo que solo escribió el caso normal y los inválidos habría visto todo en verde y habría entregado un error. Los errores de comparación (< contra <=) solo se manifiestan en el límite exacto, no en valores intermedios. Por eso cada límite del criterio merece su caso.
Alguien cambia > por >= en la regla de duración máxima y los casos normal e inválidos siguen en verde. ¿Qué conclusión es correcta?
Definition of Done
Los criterios de aceptación dicen cuándo esta historia hace lo que prometía. Pero una historia puede cumplirlos todos y seguir sin estar lista para entregarse: sin revisar, sin integrar, sin pruebas. Para eso existe la Definition of Done (DoD): una lista de chequeo que el equipo acuerda una vez y aplica a todas sus historias.
| Criterios de aceptación | Definition of Done | |
|---|---|---|
| Pertenece a | Una historia concreta. | Todo el equipo, para todas las historias. |
| Responde | ¿Hace lo que el usuario necesitaba? | ¿Está bien hecha y lista para entregarse? |
| La define | El Product Owner con el equipo, por historia. | El equipo, de común acuerdo, una vez. |
| Ejemplo en EventoUAB | "Una reserva de más de 4 horas se rechaza." | "El código pasó por Pull Request con al menos una revisión." |
Una historia está terminada cuando: (1) cumple todos sus criterios de aceptación; (2) tiene pruebas automáticas para sus casos normales, inválidos y de borde, y pasan; (3) el código fue revisado por al menos otra persona en un Pull Request; (4) está integrada a main sin romper nada; y (5) se probó en el ambiente de pruebas.
Si "revisado" se salta cuando hay apuro, deja de ser parte de la definición de terminado y se vuelve un deseo. Una historia que no cumple la DoD no está terminada: vuelve al tablero, no cuenta para la velocidad del sprint, y es mejor mostrarla así en la Review que mostrarla como completa.
¿Cada condición es un criterio de aceptación de una historia, o un ítem de la Definition of Done del equipo?
Una historia cumple todos sus criterios de aceptación, pero nadie revisó el código ni lo integró a main. Según la DoD del equipo, ¿en qué estado está?
Ejercicios de razonamiento
En estas preguntas no se te pide recordar una definición: se te da un escenario y debés razonar la respuesta antes de comprobarla.
Mateo escribió el criterio de "cancelar una reserva": "Dado que el club tiene una reserva futura, cuando la cancela, entonces el espacio queda libre." Ahora arma sus casos de prueba.
¿Cuál de estos casos es un caso inválido (y no uno normal ni uno de borde)?
El equipo tiene 30 pruebas para "reservar un espacio". Al mirarlas, todas son variantes del caso normal: distintos clubes, distintos espacios, distintos horarios, siempre libres y siempre de 1 o 2 horas.
¿Qué se puede decir del riesgo de que haya errores sin detectar?
Actividades de repaso
Un repaso integrador de todo el módulo. Cada actividad se corrige al instante; tu progreso se guarda automáticamente en este navegador.
¿Por qué los casos de borde suelen encontrar errores que los casos normales dejan pasar?
La Definition of Done es distinta para cada historia y la escribe el Product Owner junto con los criterios de aceptación.
¿Qué diferencia hay entre los criterios de aceptación y la Definition of Done?
Faltando poco para cerrar el sprint, una prueba automática falla en main. La historia cumple sus criterios y la DoD incluye "las pruebas pasan". ¿Qué corresponde?
Cheat Sheet
| Término | Idea clave |
|---|---|
| Criterio → prueba | Dado = preparar, Cuando = ejecutar, Entonces = comprobar. Cada criterio genera uno o más casos. |
| Caso normal | El camino feliz: datos correctos y resultado esperado. |
| Caso inválido | Datos o situaciones que el sistema debe rechazar con un error claro. |
| Caso de borde | El valor exacto del límite de una regla (y apenas por encima o por debajo). Detecta errores de comparación. |
| Criterios de aceptación | Condiciones de una historia concreta: ¿hace lo que el usuario necesitaba? |
| Definition of Done | Lista común del equipo, aplicada a toda historia: ¿está bien hecha y lista para entregarse? No se negocia por apuro. |
¿Sabías que...?
📏 El análisis de valores límite es de los años 70
La técnica de probar justo en los límites de una regla se popularizó con el libro The Art of Software Testing, de Glenford Myers (1979), y sigue siendo una de las más enseñadas en testing.
📘 La DoD está en la Scrum Guide
Scrum define a la Definition of Done como el compromiso del Incremento: si un elemento no la cumple, no puede presentarse en la Sprint Review ni considerarse parte del producto entregado.
🧭 Lo que viene en la próxima guía
Con historias terminadas de verdad, llega el momento de mirar la evidencia: cómo adaptar el backlog a partir de lo que reveló el sprint, y cuándo ayuda (y cuándo estorba) un patrón como Repository con inyección de dependencias.