Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

Pruebas

ZeroClaw utiliza una taxonomía de pruebas de cinco niveles respaldada por la estructura del sistema de archivos. Cada nivel tiene un límite diferente y un costo diferente; elige el nivel más bajo que demuestre lo que necesitas demostrar.

Cuando un PR reclama un comportamiento que un usuario ejecuta, hace clic, envía, instala u observa directamente, usa User-boundary proof para identificar la prueba o comprobación manual más pequeña que alcance ese límite.

Los cinco niveles

NivelQué pruebaLímiteDónde se encuentra
UnidadUna sola función o estructuraTodo simuladoBloques #[cfg(test)] en src/** o tests.rs en el mismo directorio
ComponenteUn subsistema dentro de su propio límiteSubsystem real, todo lo demás simuladotests/component/
IntegraciónMúltiples componentes internos conectados entre síInterna real, APIs externas simuladastests/integration/
SistemaSolicitud completa → respuesta a través de todos los límites internosSolo APIs externas simuladastests/system/
En vivoPila completa con servicios externos realesNada simulado, #[ignore]tests/live/

Más dos directorios que no son de pruebas:

DirectorioPropósito
tests/manual/Scripts de prueba ejecutados por humanos (shell, Python), que se ejecutan directamente, no a través de cargo
tests/support/Infraestructura compartida de mocks, no un binario de pruebas, incluida como mod support; desde cada nivel

Ejecutando pruebas

sh

cargo test                                  # unidad + componente + integración + sistema
cargo test --lib                            # solo unidad
cargo test --test component                 # solo componente
cargo test --test integration               # solo integración
cargo test --test system                    # solo del sistema
cargo test --test live -- --ignored         # live (requiere credenciales de API)
cargo test --test integration agent         # filtro dentro de un nivel
cargo nextest run --locked --workspace --exclude zeroclaw-desktop  # qué ejecuta el CI
./scripts/ci/parallel_runtime_test_gate.sh  # pruebas repetidas de tiempo de ejecución/canal en el mismo proceso
./dev/ci.sh all                             # batería completa de CI (Docker)
./dev/ci.sh firmware-protocol               # puerta de enlace de host autónoma para el protocolo de firmware (Docker)
./dev/ci.sh test-component                  # comandos de CI específicos por nivel (Docker)

El comando firmware-protocol verifica el crate independiente firmware/zeroclaw-fw-protocol, que se encuentra fuera del espacio de trabajo raíz de Cargo. scripts/ci/firmware_protocol_gate.sh es la definición canónica de sus verificaciones de formato, Clippy estricto y pruebas con bloqueo; el CI requerido y el hook pre-push invocan el mismo helper.

La puerta de ejecución paralela repite los binarios de prueba completos de las bibliotecas zeroclaw-runtime y zeroclaw-channels con 16 hilos de arnés. Ejecutar los binarios completos es intencional: detecta interferencias entre pruebas que mutan estado y turnos de agente que de otro modo no están relacionados, las cuales las ejecuciones de pruebas filtradas no pueden exponer. Las ejecuciones de CI obligatorias ejecutan esta puerta en un trabajo separado para cambios en cualquiera de los dos crates, los manifiestos de dependencias del espacio de trabajo o los propios archivos de CI de la puerta. Los demás PRs la omiten; las inserciones en master y las ejecuciones de la cola de fusión conservan el respaldo completo de regresión. Reemplaza las repeticiones con ZEROCLAW_PARALLEL_TEST_RUNS y los hilos de arnés con ZEROCLAW_PARALLEL_TEST_THREADS.

Elegir un nivel para una nueva prueba

  1. ¿Probando un subsistema de forma aislada? → tests/component/
  2. ¿Probando múltiples componentes conectados entre sí? → tests/integration/
  3. ¿Probando el flujo completo del mensaje de extremo a extremo? → tests/system/
  4. Requiere claves de API reales? → tests/live/ con #[ignore]

Después de crear el archivo, agrégalo al mod.rs del nivel y utiliza la infraestructura compartida de tests/support/.

Infraestructura compartida

Cada binario de prueba incluye mod support;, lo que hace que los mocks compartidos estén disponibles como crate::support::*.

MóduloContenido
mock_model_provider.rsMockModelProvider (con guion FIFO), RecordingModelProvider (captura solicitudes), TraceLlmModelProvider (reproducción de fixtures JSON)
mock_tools.rsEchoTool, CountingTool, FailingTool, RecordingTool
mock_channel.rsTestChannel (captura los envíos, registra los eventos de escritura)
helpers.rsmake_memory(), make_observer(), build_agent(), text_response(), tool_response(), StaticRecallMemory
trace.rsTipos LlmTrace, TraceTurn, TraceStep + LlmTrace::from_file()
assertions.rsverify_expects() para la aserción de trazas declarativas

Uso típico:

#![allow(unused)]
fn main() {
use crate::support::{MockModelProvider, EchoTool, CountingTool};
use crate::support::helpers::{build_agent, text_response, tool_response};
}

Fixtures de trazas JSON

Los fixtures de trazas son scripts predefinidos de respuestas de LLM almacenados como archivos JSON en tests/fixtures/traces/. Reemplazan la configuración de mocks en línea con scripts de conversación declarativos, mucho más fáciles de leer y editar que las cadenas de mockall.

Cómo funciona:

  1. TraceLlmModelProvider carga un fixture e implementa el trait ModelProvider.
  2. Cada llamada a provider.chat() devuelve el siguiente paso del fixture en orden FIFO.
  3. Las herramientas reales se ejecutan normalmente (EchoTool procesa realmente sus argumentos).
  4. Después de todas las rondas, verify_expects() verifica las aserciones declarativas.
  5. Si el agente llama al proveedor más veces de las que hay pasos, la prueba falla.

Formato de fixture:

{
  "nombre_modelo": "nombre-de-prueba",
  "giros": [
    {
      "entrada de usuario": "Mensaje del usuario",
      "pasos": [
        {
          "respuesta": {
            "tipo": "texto",
            "contenido": "Respuesta del LLM",
            "tokens_de_entrada": 20,
            "tokens_de_salida": 10
          }
        }
      ]
    }
  ],
  "espera": {
    "response_contains": ["texto esperado"],
    "herramientas_usadas": ["echo"],
    "max_tool_calls": 1
  }
}

Tipos de respuesta: "text" (texto plano) o "tool_calls" (solicitudes de herramientas LLM).

Espera los campos: response_contains, response_not_contains, tools_used, tools_not_used, max_tool_calls, all_tools_succeeded, response_matches (regex).

Convenciones de pruebas en vivo

Las pruebas en vivo acceden a servicios externos reales y cuestan dinero real; están marcadas con #[ignore] por defecto y solo se ejecutan con aceptación explícita.

  • Siempre #[ignore]. Nunca permitas que una prueba en vivo se ejecute durante un cargo test normal.
  • Lee las credenciales desde env::var("ZEROCLAW_TEST_*"). No leas la configuración del operador; las pruebas en vivo deben ser herméticas.
  • Ejecuta con cargo test --test live -- --ignored --nocapture.

Las pruebas de base de datos son pruebas de integración

No simules SQLite con mocks en tests que ejercitan el esquema o SQL; los tests de integración deben usar una base de datos real. La clase de bugs “el mock pasa pero producción falla” es real y ya la hemos sufrido antes.

Pruebas manuales

tests/manual/ contiene scripts para pruebas realizadas por humanos que no pueden automatizarse mediante cargo test. Ejecútalos directamente. Las pruebas de humo manuales específicas del canal se encuentran bajo tests/manual/<channel>/.