Tema: testing
-
Comparar el rendimiento de un cambio: calentamiento, repeticiones, varianza y qué reportar
Una comparación de tiempos solo es un resultado si sobrevive al ruido: fijar la carga de trabajo, descartar las ejecuciones de calentamiento, intercalar muchas repeticiones de cada variante, elegir el estadístico antes de mirar los datos, y reportar la dispersión y el entorno junto a cada número. Una diferencia menor que la dispersión entre ejecuciones no es un hallazgo.
-
Refactorizar en pasos pequeños y verificados
Refactorizar cambia la estructura sin cambiar el comportamiento; hacerlo en pasos diminutos con las pruebas en verde entre paso y paso, y separar los commits de refactorización de los cambios de comportamiento, lo mantiene seguro y revisable.
-
Escribir un informe de error útil
Un informe de error es útil cuando una persona ajena puede reproducir el fallo sin necesidad de preguntar: un título preciso, el entorno con sus versiones, pasos numerados para reproducirlo, el resultado esperado y el real por separado, el mensaje de error textual y un ejemplo lo más reducido posible. Las hipótesis sobre la causa van en una sección aparte.
-
Datos semilla y fixtures para bases de datos locales: pequeños, idempotentes y versionados junto con el esquema
Separar los datos de referencia (necesarios en todas partes), los datos de ejemplo (para desarrollo y demostraciones) y los datos de prueba (creados por los propios tests); escribir la siembra como código idempotente con upserts sobre claves naturales, mantener el conjunto de ejemplo pequeño y nombrado, ejecutarla después de las migraciones tanto en el script de configuración como en la integración continua, y no sembrar nunca las máquinas de desarrollo con datos de producción sin anonimizar.
-
Pruebas basadas en propiedades con entradas generadas
En lugar de ejemplos elegidos a mano, una prueba basada en propiedades enuncia un invariante y deja que una biblioteca genere muchas entradas, reduciendo los fallos a contraejemplos mínimos; Hypothesis es la implementación de referencia para Python.
-
Generar, criticar, revisar: cuándo un bucle de autoverificación compensa su coste
Un bucle en el que el modelo critica y revisa su propia salida mejora los resultados cuando la crítica dispone de una señal externa (tests, un validador, una fuente) y de una rúbrica fija; sin ella, los resultados publicados muestran que puede degradar las respuestas, y cada ronda añade al menos dos llamadas cuya entrada crece con el borrador.
-
Escribir una prueba unitaria en JUnit 5 y xUnit.net: anotaciones, ciclo de vida y casos parametrizados en paralelo
JUnit Jupiter marca las pruebas con @Test, ejecuta @BeforeEach y @AfterEach alrededor de cada una sobre una instancia nueva de forma predeterminada, y conduce los casos basados en datos con @ParameterizedTest más una anotación de fuente; xUnit.net usa [Fact], el constructor e IDisposable para la configuración por prueba sobre una instancia nueva, [Theory] con [InlineData] para los casos, y fixtures para el contexto costoso compartido. Escribir pruebas con la misma forma en ambos mantiene alineadas las convenciones de un equipo políglota.
-
Diseñar un pipeline de integración continua
Un pipeline de CI debería ser rápido, autocomprobable e idéntico para cada cambio: compilar desde un checkout limpio, ejecutar linters y pruebas por etapas, fallar con claridad, y mantener el tiempo total lo bastante corto para que se espere a que termine.
-
Bases de datos efímeras en contenedores para pruebas de integración
Iniciar el motor de base de datos real en un contenedor desechable por cada ejecución de pruebas, mantener sus datos en memoria, aplicar las migraciones una sola vez a una base de datos plantilla y copiarla por cada archivo de pruebas; así las pruebas ejercitan el planificador real, las restricciones y el comportamiento de aislamiento en lugar de un sustituto en memoria.
-
Usar pruebas basadas en propiedades para parsers
Expresar los invariantes del parser sobre entradas generadas y conservar los fallos minimizados como ejemplos de regresión focalizados.
-
pass^k sobre ensayos repetidos predice mejor los incidentes de agentes en producción que pass@k
Hipótesis: para agentes desplegados en tareas repetitivas, la tasa de éxito en todos los ensayos (pass^k) sobre un conjunto de evaluación se correlaciona más fuertemente con la tasa de ejecuciones fallidas o escaladas en producción que la tasa de éxito en algún ensayo (pass@k), porque en producción cada tarea dispone de un único intento.
-
Reproducibilidad de un experimento de aprendizaje automático: semillas, entorno, datos y los límites del determinismo
Volver a ejecutar un experimento y obtener el mismo número requiere estados aleatorios fijos y pasados explícitamente, versiones de bibliotecas fijadas, un conjunto de datos y una partición identificados, y ser consciente de que los kernels de GPU y las nuevas versiones de las bibliotecas aún pueden cambiar los resultados; el protocolo hace que las ejecuciones sean repetibles donde es posible y documenta dónde no lo son.
-
Hacer red-teaming a un flujo de trabajo de agente antes de otorgarle permisos reales
Atacar al agente de la misma forma en que lo harán el contenido y los usuarios: inyección de prompt indirecta a través de cada entrada que lee, manipulación de argumentos de herramientas, exfiltración mediante llamadas a herramientas y agotamiento de presupuesto; ejecutar sondas guionizadas más intentos manuales, registrar lo que hizo el agente, y corregir el límite (boundary), no solo el prompt.
-
Datos de prueba sin datos personales de producción
Dar a desarrollo, CI y staging datos realistas generándolos: clasificar las columnas, escribir generadores con semilla que pasen los validadores de la aplicación, producir volumen con generate_series, copiar de producción solo las distribuciones, marcar los registros generados de forma reconocible, y eliminar el atajo de volcar producción.
-
¿Cuánta cobertura de pruebas es suficiente para un servicio pequeño?
Pregunta abierta: para un servicio de unos pocos miles de líneas con una base de datos y una API HTTP, ¿qué nivel de cobertura y qué combinación de pruebas se ha observado que mantiene tasas de defectos aceptables sin ralentizar los cambios?
-
Extracción estructurada de documentos con JSON Schema, validación y reintentos acotados
Definir el registro objetivo como un JSON Schema con additionalProperties false, pedirle al modelo exactamente esa forma, validar cada respuesta con un validador real, reintentar un número acotado de veces incluyendo el error de validación en el prompt, y enrutar hacia una persona lo que siga fallando en lugar de adivinar.
-
Revisar código escrito por un agente de IA
Un protocolo de revisión propuesto para cambios generados: comparar el diff con la solicitud, confirmar que cada API y dependencia existe, verificar las afirmaciones sobre las pruebas ejecutándolas y rompiéndolas, leer las pruebas antes que la implementación, buscar errores silenciados, y registrar qué se comprobó.
-
El ciclo del desarrollo guiado por pruebas
Escribir una prueba que falle, hacerla pasar con el cambio más simple y luego refactorizar con las pruebas en verde; el ciclo mantiene pequeñas las decisiones de diseño y da una razón de ser a cada línea.
-
Selectores estables y espera automática en pruebas de extremo a extremo de navegador
Dos causas dominan las pruebas de navegador inestables (flaky): selectores que se rompen o coinciden con el elemento equivocado, y esperas fijas (sleeps) que dan por supuesto el tiempo. Localizar los elementos por rol, etiqueta o id de prueba, hacer que cada localizador coincida exactamente con un elemento, y sincronizar mediante aserciones con reintento y comprobaciones de accionabilidad en lugar de tiempo.
-
Ejecutar pruebas de mutación sin ahogarse en supervivientes
Ejecutar una herramienta de mutación sobre un módulo, clasificar cada mutante superviviente como una aserción faltante, un caso faltante o un mutante equivalente, corregir los dos primeros, excluir el tercero, y acotar el tiempo de ejecución con ejecuciones incrementales o limitadas al diff; usar la puntuación como un trinquete por módulo en lugar de un objetivo global.
Legible por máquina: JSON