⬅️ Volver a la clase 173 · 🧬 Primos del Atlas · 🧟 Índice de lenguajes vivos · 📚 Índice
Comprobar el sistema entero con una entrada y una salida esperada: e2e=pasa. Es lo que hace el
verificador de este curso desde la clase 040, y lo que hacen las pruebas de extremo a extremo de
cualquier sistema. Y estos lenguajes aportan aquí la técnica más antigua y más eficaz que existe para
esto: preparar unos ficheros de entrada, ejecutar el sistema y comparar la salida con una guardada —
que es de los años sesenta y sigue siendo insuperable.
🎯 Estos lenguajes no están aquí por ser antiguos
El criterio es doble y se declara en la ficha de cada uno: que se ejecute hoy —banca, sanidad, aviónica, ERP, diseño de chips, CAD— y que deje a la vista un concepto que los diez del núcleo esconden.
Aquí el concepto es la prueba del sistema completo, y estos lenguajes la enseñan porque sus sistemas no se pueden probar de otra manera: un lote de veinte pasos, una transacción que toca cuatro programas y dos bases de datos, o un cálculo que corre en mil procesos. Y de ahí salieron las técnicas que esta parte del curso ha ido nombrando: la comparación de salidas (clase 140), la ejecución en paralelo y Expect para lo que no tiene API (clase 147).
Y aparece la tensión que define esta clase: estas pruebas son las más valiosas y las más frágiles.
Y ninguno es una foto fija: casi todos han incorporado en los últimos años JSON, REST, GPU, Unicode o Git. Cada ficha lo detalla en su sección
🔄 Lo que se ha modernizado.
a b esperado → stdout: e2e=<pasa|falla>pasa si el sistema (a + b) da el esperado| stdin | esperado |
|---|---|
3 4 7 |
e2e=pasa |
2 2 5 |
e2e=falla |
10 5 15 |
e2e=pasa |
Qué está verificado en esta página. Los lenguajes de la sección 🟢 se ejecutan en CI contra este mismo
casos.json, igual que las diez implementaciones del núcleo (workflow Labs). Los de la sección 🟡 no pueden cumplir este contrato sin falsear el lenguaje, y se explica por qué. Los de la sección ⚪ sí podrían, pero su cadena de herramientas no está en los runners: son correctos, sin sello de máquina.
Ficha completa · Banca, seguros, gobierno, medios de pago · cobc -x -free prog.cob
IDENTIFICATION DIVISION.
PROGRAM-ID. E2E.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 LINEA PIC X(60).
01 C-A PIC X(15).
01 C-B PIC X(15).
01 C-E PIC X(15).
01 A PIC S9(9) COMP.
01 B PIC S9(9) COMP.
01 ESPER PIC S9(9) COMP.
PROCEDURE DIVISION.
ACCEPT LINEA
UNSTRING LINEA DELIMITED BY ALL SPACES
INTO C-A C-B C-E
END-UNSTRING
COMPUTE A = FUNCTION NUMVAL(C-A)
COMPUTE B = FUNCTION NUMVAL(C-B)
COMPUTE ESPER = FUNCTION NUMVAL(C-E)
IF A + B = ESPER
DISPLAY "e2e=pasa"
ELSE
DISPLAY "e2e=falla"
END-IF
STOP RUN.
COBOL y las pruebas de extremo a extremo. El mundo del lote inventó la técnica del gancho, y merece verla completa porque es más elaborada de lo que parece:
//PRUEBA JOB
//COPIAR EXEC PGM=IEBGENER <-- preparar los datos de entrada conocidos
//SISUT1 DD DSN=PRUEBA.ENTRADA.CASO01,DISP=SHR
//SISUT2 DD DSN=&&ENTRADA,DISP=(NEW,PASS)
//EJECUTA EXEC PROC=MIPROCESO <-- ejecutar el sistema completo
//COMPARA EXEC PGM=ISRSUPC <-- comparar la salida con la esperada
//NEWDD DD DSN=&&SALIDA,DISP=SHR
//OLDDD DD DSN=PRUEBA.ESPERADA.CASO01,DISP=SHR
//OUTDD DD SYSOUT=*
ISRSUPC —SuperC— es el comparador de IBM, y es lo que hace esta técnica práctica, porque tiene una
capacidad que un diff normal no tiene:
CMPCOLM 1:60,80:100 <-- comparar SOLO estas columnas
DPLINE '2026-' <-- IGNORAR las líneas que contengan una fecha
Poder excluir columnas y líneas es lo que hace usable la comparación de salidas, porque toda salida real contiene cosas que cambian en cada ejecución: fechas, horas, números de trabajo, contadores.
Es exactamente lo que Test::Deep con ignore() hace en Perl (clase 140), con cuarenta años de
adelanto.
Y merece señalar la segunda regla del cierre aplicada a este mundo, porque es donde falla: los datos de prueba.
✗ Ejecutar la prueba contra la base de datos de desarrollo compartida.
→ otro equipo cambia un cliente y la prueba falla mañana.
✓ Cada caso restaura sus propias tablas antes de ejecutarse,
desde un juego de datos versionado.
Y esa restauración es lo que hace que las pruebas de lote sean reproducibles, y es la práctica estándar en los sistemas serios: juegos de datos de prueba tratados como código (clase 145), versionados y con dueño.
Ficha completa · HPC, clima, física, BLAS/LAPACK · gfortran -O2 prog.f90
program e2e
implicit none
integer :: a, b, esperado
read(*, *) a, b, esperado
if (a + b == esperado) then
write(*, '(A)') 'e2e=pasa'
else
write(*, '(A)') 'e2e=falla'
end if
end program e2e
Fortran y las pruebas de extremo a extremo. El cálculo científico tiene la versión más difícil de esta clase, y ya apareció en la clase 140: la salida nunca es idéntica.
Comparar dos ejecuciones de una simulación:
- con distinto compilador → el último dígito cambia
- con distinto número de procesos → el orden de las sumas cambia
- con la misma máquina y la misma versión → normalmente sí coincide
Así que la comparación byte a byte no sirve, y la técnica de este dominio es de tres niveles y merece verla porque es un buen modelo:
Nivel 1 — pruebas de regresión con tolerancia:
Caso pequeño, resultado guardado, y comparación con tolerancia RELATIVA
justificada por el análisis del error (clase 140), no por lo que hizo falta.
Nivel 2 — magnitudes conservadas:
! La masa total, la energía y el momento DEBEN conservarse
if (abs(masa_final - masa_inicial) / masa_inicial > 1e-12_dp) error stop
Esas comprobaciones no dependen del valor exacto, así que son robustas frente al compilador y al paralelismo — y detectan la mayoría de los errores reales.
Nivel 3 — soluciones analíticas:
Para unos pocos casos existe la solución exacta (una onda plana, un flujo laminar).
Comparar contra ella verifica el MÉTODO, no solo la ausencia de cambios.
Y la diferencia entre el nivel 1 y el nivel 3 es la de esta clase: el nivel 1 detecta que algo cambió; el nivel 3 detecta que algo está mal.
Y merece cerrar con la práctica que este dominio ha adoptado y que es la primera regla del cierre: la pirámide de casos.
En cada cambio: casos de segundos, con 2 procesos
Cada noche: casos de minutos, con varias combinaciones
Antes de publicar: el caso de producción, en el clúster (clase 147)
Y lo importante es que el nivel de arriba exista y se ejecute alguna vez, porque es el único que prueba lo que de verdad se usa — y muchos proyectos se quedan solo en el primero.
Ficha completa · Aviónica, espacio, ferrocarril, defensa · gnatmake prog.adb
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure E2e is
A, B, Esperado : Integer;
begin
Get (A);
Get (B);
Get (Esperado);
if A + B = Esperado then
Put_Line ("e2e=pasa");
else
Put_Line ("e2e=falla");
end if;
end E2e;
Ada y las pruebas de extremo a extremo. En los dominios de Ada, esta clase tiene un nombre formal y un peso enorme: la verificación del sistema integrado.
Niveles de prueba en un proyecto certificado:
1. Unitaria → cada subprograma, con cobertura MC/DC (clase 139)
2. Integración → los componentes entre sí
3. Hardware-software → el software sobre el hardware REAL
4. Sistema → el equipo completo, en banco de pruebas
5. Y vuelo o campo → el sistema en su entorno
Y cada nivel tiene sus requisitos trazados (clase 166): cada requisito de alto nivel se verifica en el nivel que le corresponde, y una herramienta comprueba que no falte ninguno.
Y este dominio aporta a esta clase la técnica que hace posible probar lo que no se puede ejecutar de verdad, y merece explicarla: la prueba con hardware simulado en el bucle.
El sistema real ejecuta su software, y en lugar de sensores y actuadores
tiene conectado un SIMULADOR que:
- le da lecturas de sensor como si volara
- recibe sus órdenes de actuador
- y simula la física del vehículo en tiempo real
Y así se prueban situaciones que no se pueden provocar de verdad: un fallo de motor, una ráfaga extrema, un sensor que miente.
Es la versión física de los objetos simulados de la clase 139, y su valor es el mismo: poder ejercitar los caminos de error.
Y merece señalar la propiedad que la tercera regla del cierre pide y que este mundo consigue mejor que nadie: el determinismo.
Con Ravenscar (clase 146) y sin reserva dinámica,
el sistema es determinista: la misma entrada da la misma secuencia de ejecución.
Y eso hace que una prueba que falla se pueda REPRODUCIR.
Una prueba de extremo a extremo reproducible es una herramienta; una intermitente es un impuesto — y la diferencia, en gran parte, viene de las decisiones de diseño de la clase 135, no de la prueba.
Ficha completa · Escritorio empresarial, TPV, industria (vía Delphi y Free Pascal) · fpc -Mobjfpc prog.pas
program E2e;
{$MODE OBJFPC}{$H+}
var
A, B, Esperado: Integer;
begin
Read(A, B, Esperado);
if A + B = Esperado then
WriteLn('e2e=pasa')
else
WriteLn('e2e=falla');
end.
Pascal y las pruebas de extremo a extremo. El ecosistema Delphi tiene el problema de esta clase en su forma clásica: probar una aplicación de escritorio con interfaz gráfica.
{ Automatización de la interfaz: TestComplete, Ranorex, o la API de Windows }
FindWindow('TForm1', 'Mi aplicación');
SendMessage(Handle, WM_COMMAND, ...);
Y merece decir con franqueza que esas pruebas son las más frágiles que existen: dependen de posiciones, de nombres de control, de la velocidad de la máquina y del tema visual.
Y la lección de esta clase, que vale para cualquier interfaz gráfica o web, es la primera regla del cierre: pocas y bien elegidas.
✗ Probar cada formulario y cada validación por la interfaz.
→ miles de pruebas lentas y frágiles.
✓ Probar la LÓGICA por debajo (clase 139),
y por la interfaz solo unos pocos caminos completos:
"entrar, crear un pedido, cobrarlo, imprimir el ticket".
Y para que eso sea posible, la lógica tiene que estar separada de la interfaz (clase 149) — que es, otra vez, buena arquitectura y comprobabilidad siendo lo mismo.
Y el ecosistema tiene una técnica que merece nombrarse y que resuelve la segunda regla del cierre: la base de datos en memoria o en fichero temporal.
{ Cada prueba arranca con su propia base, creada desde un guion }
FDConnection.Params.Database := TempDir + 'prueba_' + GUID + '.fdb';
EjecutarGuion('esquema.sql');
EjecutarGuion('datos_de_prueba.sql');
Una base por prueba, creada y destruida, elimina de golpe las pruebas intermitentes por estado compartido — y con SQLite o Firebird embebido cuesta milisegundos.
Es la aplicación más directa de "datos propios y desechables", y merece señalar que la mayoría de los equipos que sufren pruebas intermitentes no han probado esto.
Ficha completa · IA simbólica, CAD, investigación · sbcl --script prog.lisp
(let ((a (read))
(b (read))
(esperado (read)))
(format t "e2e=~A~%" (if (= (+ a b) esperado) "pasa" "falla")))
Lisp y las pruebas de extremo a extremo. Lisp aporta a esta clase una técnica que su modelo hace fácil y que merece destacarse porque ataca la fragilidad del cierre: grabar y reproducir.
;; Envolver una función para GRABAR sus llamadas y sus resultados en producción
(defun grabar (nombre fn)
(lambda (&rest args)
(let ((r (apply fn args)))
(push (list nombre args r) *grabacion*)
r)))
;; Y luego reproducir: la misma secuencia, sin el sistema externo
Grabar las interacciones reales con los sistemas externos y reproducirlas convierte una prueba de extremo a extremo —lenta, dependiente de la red y frágil— en una prueba rápida y determinista.
Es la técnica que en otros ecosistemas se llama VCR o cassettes, y que Lisp permite montar en veinte líneas porque redefinir una función es una operación normal (clase 139).
Y la advertencia que va con ella y merece decirse: una prueba con grabación deja de detectar cambios en el sistema externo.
Así que hacen falta las dos: la mayoría con grabación —rápidas y estables— y unas pocas contra el sistema real, ejecutadas menos veces (clase 147).
Y el ecosistema para esta clase:
(asdf:test-system "mi-sistema")
(uiop:run-program (list "./servicio" "--puerto" "8080") :wait nil)
(dex:get "http://localhost:8080/pedidos/1")
uiop:run-program con :wait nil lanza el sistema en segundo plano, y el guion de prueba puede
esperar, ejercitar y parar — que es el patrón de la clase 165.
Y merece cerrar con la aportación de Lisp que la Parte 8 hace posible y que en esta clase es muy valiosa: cuando una prueba de extremo a extremo falla, se puede entrar.
;; La prueba falla → el depurador se abre CON el estado vivo (clase 141)
;; y se puede inspeccionar el sistema entero en el punto del fallo
Diagnosticar un fallo de una prueba de sistema sin reproducirlo a mano es lo que más tiempo ahorra en esta clase, y es exactamente lo que un depurador sobre el proceso vivo permite.
Ficha completa · Diseño de chips (EDA), redes, testing · tclsh prog.tcl
gets stdin linea
lassign [string trim $linea] a b esperado
puts "e2e=[expr {$a + $b == $esperado ? {pasa} : {falla}}]"
Tcl y las pruebas de extremo a extremo. Este es el componente de Tcl por excelencia en esta parte (clase 165), y merece juntar aquí la receta completa con las tres reglas del cierre.
package require tcltest
namespace import ::tcltest::*
# 1. Datos propios y desechables (regla 2)
proc arrancarSistema {} {
set ::dir [file tempdir]
exec sqlite3 $::dir/prueba.db < esquema.sql
set ::api [exec ./api --db $::dir/prueba.db --puerto 0 --puertofile $::dir/p &]
esperarFichero $::dir/p -timeout 10 ;# 2. esperar EVENTOS (regla 3)
set ::puerto [leerFichero $::dir/p]
}
proc pararSistema {} {
exec kill $::api
file delete -force $::dir
}
test flujo-completo-1.1 {crear un pedido y cobrarlo} -setup {
arrancarSistema
} -body {
set id [crearPedido $::puerto {items {A1 2}}]
cobrar $::puerto $id
dict get [consultar $::puerto $id] estado
} -cleanup {
pararSistema
} -result {cobrado}
cleanupTests
Y tres detalles de ese guion merecen destacarse porque son las decisiones que hacen la prueba sostenible:
--puerto 0 y un fichero con el puerto real. Pedir un puerto fijo es la causa clásica de pruebas
que fallan al ejecutarse en paralelo (clase 147): que el sistema elija y lo publique permite
ejecutar veinte a la vez.
esperarFichero en lugar de dormir (clase 171). Nunca after 3000.
Y -cleanup que se ejecuta siempre, incluso si el cuerpo falla — que es la propiedad que evita que
una prueba rota deje procesos y directorios por todas partes.
Y Tcl aporta lo que la clase 147 ya señaló y que aquí es la herramienta para lo que no tiene API: Expect.
Probar de extremo a extremo una aplicación de terminal, un instalador o un equipo de red es algo que solo Expect hace bien, y sigue siendo la respuesta treinta y cinco años después.
Ficha completa · Sysadmin, texto, bioinformática · perl prog.pl
use strict;
use warnings;
my $linea = <STDIN>;
chomp $linea;
my ($a1, $b1, $esperado) = split ' ', $linea;
print "e2e=", ($a1 + $b1 == $esperado ? 'pasa' : 'falla'), "\n";
Perl y las pruebas de extremo a extremo. Perl tiene el ecosistema más completo de esta página para esta clase, y merece verlo porque cubre las tres reglas del cierre:
use Test::More;
use Test::TCP; # ← puerto libre y arranque de servidor
use Test::PostgreSQL; # ← ¡una base de datos temporal por prueba!
use Test::Deep; # comparación con comodines (clase 140)
my $pg = Test::PostgreSQL->new; # arranca su propio PostgreSQL
test_tcp(
server => sub {
my $puerto = shift;
exec './api', '--dsn', $pg->dsn, '--puerto', $puerto;
},
client => sub {
my $puerto = shift;
my $r = pedir("http://127.0.0.1:$puerto/pedidos", {items => 2});
cmp_deeply($r, {
id => ignore(), # cambia en cada ejecución
creado => re(qr/^\d{4}-/),
total => num(24.20, 0.01),
}, 'el pedido creado cumple el contrato');
},
);
done_testing();
Test::PostgreSQL merece destacarse porque resuelve la segunda regla del cierre de la forma más
limpia: arranca una instancia de PostgreSQL propia, en un directorio temporal, y la destruye al
terminar.
Cada prueba tiene su base de datos entera, aislada, y se pueden ejecutar en paralelo sin coordinación.
Es una idea excelente y sorprendentemente poco usada: el coste de arrancar una base vacía es de segundos, y el de depurar pruebas que se pisan es de días.
Y Test::TCP resuelve el problema del puerto igual que Tcl en esta página: busca uno libre y se lo
pasa al servidor.
Y merece cerrar con la observación que la clase 147 anticipó y que en esta clase es la regla que sostiene todo: una prueba intermitente hay que arreglarla o borrarla.
# ✗ lo que NO hay que hacer, y todo el mundo acaba haciendo
$ENV{REINTENTOS} = 3;
Reintentar hasta que pase convierte la suite en un generador de ruido, y a partir de ahí nadie mira el rojo — que es exactamente lo que la integración continua existía para evitar.
Y la causa suele ser una de tres: espera por tiempo, estado compartido, o dependencia del orden. Las tres tienen solución conocida, y ninguna es el reintento.
Ficha completa · Videojuegos, navegadores, finanzas, HPC · g++ -std=c++17 prog.cpp
#include <iostream>
int main() {
long long a{}, b{}, esperado{};
if (!(std::cin >> a >> b >> esperado)) return 1;
std::cout << "e2e=" << (a + b == esperado ? "pasa" : "falla") << '\n';
return 0;
}
C++ y las pruebas de extremo a extremo. C++ aporta a esta clase una técnica que la clase 141 nombró y que aquí es la respuesta al problema más difícil: el fallo que solo ocurre a veces.
rr record ./sistema_completo --caso 42
# ...falla una vez de cada cincuenta...
rr replay # ← la MISMA ejecución, exactamente
rr graba una ejecución completa y la reproduce de forma determinista, incluidas las condiciones de
carrera.
Y eso convierte una prueba intermitente en un fallo reproducible — que es la diferencia entre poder arreglarlo y no poder.
Es la mejor herramienta que existe para la tensión del "por qué" de esta clase, y merece conocerse aunque solo se use dos veces al año.
Y las otras técnicas de C++ para esta clase, ordenadas por lo que cazan:
| Técnica | Qué caza |
|---|---|
| ThreadSanitizer en la prueba de sistema | carreras que se manifiestan una vez de cada mil (clase 136) |
| AddressSanitizer | corrupción de memoria bajo carga real |
Fuzzing con libFuzzer |
entradas que nadie pensó |
| Pruebas basadas en propiedades | invariantes, con casos generados (clase 140) |
rr |
reproducir lo irreproducible |
El fuzzing merece la mención porque en un sistema que procesa entradas externas es la prueba de extremo a extremo más rentable que existe:
./api_fuzzer corpus/ -max_total_time=3600
Se generan millones de entradas aleatorias y mutadas, guiadas por cobertura, y cada caída se guarda como caso de prueba reproducible.
Y encuentra cosas que ninguna persona escribiría: cadenas vacías, números en los límites, secuencias UTF-8 inválidas, anidamientos de mil niveles.
Y para el proyecto de esta parte, la recomendación concreta es la primera regla del cierre aplicada con criterio: pocas pruebas de extremo a extremo, y en cambio fuzzing continuo sobre las fronteras — porque es donde llegan los datos hostiles (clase 153) y donde una persona escribiendo casos nunca va a competir con una máquina.
Estos lenguajes no pueden leer de stdin y escribir en stdout sin dejar de ser ellos
mismos. No es una limitación del material: es su naturaleza. El cálculo es el mismo y la forma
de entrar y salir es la de su anfitrión. No pasan por el verificador, y se dice.
Ficha completa · IBM i: ERP, retail, logística, manufactura · CRTBNDRPG sobre IBM i
En IBM i un programa recibe sus datos por parámetros, por un fichero o por una pantalla, nunca por la entrada estándar.
**free
ctl-opt dftactgrp(*no) actgrp(*caller);
dcl-pi E2E;
a int(10) const;
b int(10) const;
esperado int(10) const;
end-pi;
if a + b = esperado;
dsply 'e2e=pasa';
else;
dsply 'e2e=falla';
endif;
*inlr = *on;
return;
RPG y las pruebas de extremo a extremo. IBM i tiene un mecanismo que resuelve la segunda regla del cierre mejor que cualquier otra plataforma de esta página, y ya apareció en la clase 139: la lista de bibliotecas.
CRTLIB PRUEBA$$$
CRTDUPOBJ OBJ(*ALL) FROMLIB(PRODDATOS) OBJTYPE(*FILE) TOLIB(PRUEBA$$$) DATA(*YES)
CHGLIBL LIBL(PRUEBA$$$ MIAPP QGPL)
... ejecutar las pruebas ...
DLTLIB PRUEBA$$$
Copiar el esquema y los datos a una biblioteca temporal y redirigir el trabajo hacia ella da a cada ejecución de pruebas su propia copia completa de la base de datos, sin tocar nada.
Y es una operación del sistema, no de la aplicación: los programas no saben que están usando otras tablas.
Es inyección de dependencias a nivel de sistema operativo (clase 139), y resuelve de raíz la fuente número uno de pruebas intermitentes.
Y las otras dos reglas del cierre tienen respuesta en la plataforma:
Los datos deterministas: CRTDUPOBJ desde un juego de datos versionado, y el diario (clase 172)
permite volver al estado inicial con RMVJRNCHG en lugar de recrear.
Y el diagnóstico: si una prueba falla, el registro del trabajo tiene todo (clase 142) — cada mensaje, con su programa y su número de sentencia.
Y merece cerrar con la práctica de este mundo que la primera regla del cierre recomienda y que aquí es natural: probar por la interfaz de programa, no por la pantalla.
// La prueba llama al procedimiento del programa de servicio,
// no simula pulsaciones en una pantalla 5250
aEqual(120.50 : calcularTotal(pedidoDePrueba) : 'total con IVA');
Y para el flujo completo, un guion CL que encadena los programas (clase 171) y compara los ficheros resultantes con los esperados — que es, otra vez, la técnica de COBOL de esta página.
Sí podrían cumplir el contrato, pero su cadena de herramientas no está en los runners de CI.
Ficha completa · Mainframe z/OS: banca, seguros · IBM Enterprise PL/I for z/OS
e2e: procedure options(main);
declare (a, b, esperado) fixed binary(31);
get list (a, b, esperado);
if a + b = esperado then
put skip list ('e2e=pasa');
else
put skip list ('e2e=falla');
end e2e;
PL/I y las pruebas de extremo a extremo. PL/I aporta a esta clase la versión más ambiciosa que existe, y la clase 140 ya la nombró: la ejecución en paralelo.
Durante SEIS MESES:
- el sistema viejo y el nuevo procesan las MISMAS entradas reales
- solo el viejo tiene efectos
- y un proceso compara TODAS las salidas, todos los días
- cada discrepancia se investiga y se documenta
Es una prueba de extremo a extremo con datos de producción reales, ejecutada millones de veces.
Y merece explicar por qué se hace así y no con casos escritos, porque el argumento es fuerte: nadie puede escribir los casos que un sistema de treinta años ha visto.
Las discrepancias que aparecen son casi siempre:
- clientes con configuraciones que ya no se dan de alta
- contratos con excepciones aprobadas hace veinte años
- casos que el sistema viejo maneja mal y de los que alguien depende
Y ninguno de esos estaría en un juego de casos de prueba, porque nadie sabe que existen.
Es la lección más importante de esta clase para cualquier reescritura: los datos de producción son la única especificación completa del sistema actual.
Y la técnica que lo hace posible y que merece nombrarse: la captura y reproducción de entradas.
Se instrumenta el sistema viejo para guardar cada entrada
—cada mensaje, cada fichero, cada petición—
y se reproduce contra el nuevo, fuera de línea.
Y eso permite ejecutar seis meses de tráfico real en unas horas, cuantas veces haga falta.
Y es la aportación de esta columna a la primera regla del cierre, con un matiz: aquí las pruebas no son pocas y bien elegidas — son todas las que ocurrieron.
Y la razón es que el objetivo no es comprobar que el sistema funciona, sino que es equivalente (clase 140), y para eso la cobertura de casos reales vale más que cualquier diseño de pruebas.
Ficha completa · Sanidad: historia clínica, VistA, Epic · YottaDB
E2E ; Prueba de extremo a extremo -- clase 173
read linea
new a, b, esperado
set a = $piece(linea, " ", 1)
set b = $piece(linea, " ", 2)
set esperado = $piece(linea, " ", 3)
write "e2e=", $select(a + b = esperado : "pasa", 1 : "falla"), !
quit
M y las pruebas de extremo a extremo. M tiene, para esta clase, una capacidad que su modelo de datos hace posible y que es más fuerte que comparar salidas: comparar el estado de la base.
; 1. copiar el estado inicial a un espacio temporal
merge ^||INICIAL = ^PACIENTE(dfn)
; 2. ejecutar el flujo completo
do procesarAlta^ADT(dfn, datos)
; 3. y comparar el resultado con el esperado
write $$comparar^UTIL($name(^PACIENTE(dfn)), $name(^ESPERADO(caso)))
merge copia un subárbol entero de una global a otra en una operación — que es la forma de M de
capturar un estado.
Y merece explicar por qué comparar el estado es mejor que comparar la salida, porque es la lección de esta explicación y ya apareció en la clase 140:
Comparar la SALIDA detecta lo que el sistema dijo.
Comparar el ESTADO detecta lo que el sistema HIZO.
Y un fallo típico —actualizar mal un índice, dejar un registro huérfano,
no borrar algo temporal— no se ve en la salida y sí en el estado.
Y esa es la clase de fallo que aparece meses después, cuando alguien consulta por ese índice.
Y las dos reglas del cierre que este dominio resuelve bien:
Los datos propios: ^|| da globals temporales privadas del proceso (clase 139), y las
implementaciones modernas permiten regiones de base de datos desechables.
Y el determinismo: la trampa clásica de este mundo es la fecha.
; ✗ una prueba que usa DT (la fecha de hoy) falla el 1 de enero
if $$edad^UTIL(fechaNac, DT) > 65 ...
; ✓ la fecha se INYECTA
if $$edad^UTIL(fechaNac, fechaReferencia) > 65 ...
Depender del reloj es la segunda causa de pruebas intermitentes, después del estado compartido — y en un dominio donde casi todo se calcula respecto a hoy, es un problema constante.
Y la defensa es de diseño: el tiempo es un parámetro, no una variable global — que es una de las recomendaciones más rentables de toda esta parte del curso.
Ficha completa · Banca, seguros, trading · Pharo
| linea partes a b esperado |
linea := stdin nextLine trimBoth.
partes := linea substrings: ' '.
a := (partes at: 1) asNumber.
b := (partes at: 2) asNumber.
esperado := (partes at: 3) asNumber.
Transcript
show: 'e2e=', (a + b = esperado ifTrue: [ 'pasa' ] ifFalse: [ 'falla' ]);
cr.
Smalltalk y las pruebas de extremo a extremo. Smalltalk, que inventó las pruebas unitarias (clase 139), aporta a esta clase dos capacidades que vienen de su modelo.
La primera: la imagen como estado de prueba.
"Preparar el sistema en un estado concreto y GUARDARLO"
self cargarDatosDePrueba.
Smalltalk snapshot: true andQuit: true.
Una imagen con los datos ya cargados arranca en el estado exacto que la prueba necesita — lo que resuelve la segunda regla del cierre sin base de datos temporal ni guiones de carga.
Es la misma idea que una instantánea de contenedor (clase 174), disponible desde 1980.
Y la segunda: el fallo se puede examinar entero.
[ self ejecutarFlujoCompleto ] on: Error do: [ :e |
"Guardar el CONTEXTO del error para abrirlo después (clase 141)"
FLSerializer serialize: e signalerContext toFileNamed: 'fallo.fuel' ]
Y ese fichero se abre en el depurador en otra máquina, con la pila viva y los objetos.
Es la respuesta al problema más caro de esta clase: una prueba de sistema que falla en la integración continua y no se reproduce en local. Aquí el fallo viaja.
Y merece cerrar esta clase con la observación que Smalltalk permite hacer y que resume la parte: las pruebas de extremo a extremo son caras porque el sistema no se deja preguntar.
Si el sistema puede decir en qué estado está,
si el fallo se puede capturar entero,
y si el entorno se puede recrear exactamente,
entonces la prueba de extremo a extremo es barata y estable.
Y si no, se compensa con esperas, reintentos y capturas de pantalla.
La fragilidad de estas pruebas es, casi siempre, un síntoma del sistema y no de la prueba — y las propiedades que las abaratan son las mismas que esta parte del curso viene defendiendo: fronteras claras, estado inspeccionable, entorno reproducible y tiempo inyectado.
Lo transferible: una prueba de extremo a extremo comprueba lo que de verdad importa y falla por lo que no. De ahí las tres reglas que la hacen sostenible: pocas y bien elegidas —cubrir los caminos críticos, no todos los casos, que ya están cubiertos abajo (clase 139)—; con datos propios y desechables, porque una prueba que depende del estado que dejó otra falla de forma intermitente; y esperando por eventos, no por tiempos (clase 171), que es la causa número uno de pruebas intermitentes. Y la regla que sostiene todo: una prueba que falla a veces y se reintenta hasta que pasa ya no prueba nada — y enseña al equipo a ignorar el rojo.
⏮️ Volver a la clase 173 · 🧬 Los primos del Atlas · 🧟 Índice de lenguajes vivos