> For the complete documentation index, see [llms.txt](https://docs.neuron-ai.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.neuron-ai.dev/neuron-v3-es/agente/testing.md).

# Pruebas

Cuando pruebas un agente, no quieres que cada ejecución de prueba haga llamadas reales a la API de OpenAI, Anthropic o cualquier otro proveedor. Las llamadas reales son lentas, cuestan dinero y devuelven resultados diferentes cada vez, lo que hace que tus pruebas sean inestables y caras. Lo mismo aplica a los agentes RAG: no quieres levantar una base de datos vectorial ni llamar a una API de embeddings solo para verificar la lógica de tu agente.

Neuron incluye dobles de prueba listos para usar que solucionan este problema. `FakeAIProvider` reemplaza al proveedor de IA, `FakeEmbeddingsProvider` reemplaza al proveedor de embeddings, y `FakeVectorStore` reemplaza el almacén vectorial. Devuelven respuestas predeterminadas, nunca acceden a la red y registran cada interacción para que puedas verificar exactamente lo que hizo tu agente.

### Configuración

Crea una `FakeAIProvider` con las respuestas que esperas que devuelva el modelo, luego inyéctalo en tu agente:

```php
use NeuronAI\Chat\Messages\Stream\AssistantMessage;
use NeuronAI\Testing\FakeAIProvider;

$provider = new FakeAIProvider(
    new AssistantMessage('¡Hola! ¿En qué puedo ayudarte?')
);

$agent = MyAgent::make()->setAiProvider($provider);
```

Las respuestas se devuelven en orden. Si tu agente realiza múltiples llamadas al proveedor (por ejemplo, llamadas a herramientas), encola varias respuestas:

```php
$provider = new FakeAIProvider(
    new AssistantMessage('Primera respuesta'),
    new AssistantMessage('Segunda respuesta'),
);
```

### Chat

```php
public function test_agent_responds(): void
{
    $provider = new FakeAIProvider(
        new AssistantMessage('La capital de Francia es París.')
    );

    $agent = MyAgent::make()->setAiProvider($provider);

    $message = $agent->chat(new UserMessage('¿Cuál es la capital de Francia?'))->getMessage();

    $this->assertSame('La capital de Francia es París.', $message->getContent());
    $provider->assertCallCount(1);
}
```

### Streaming

El proveedor falso divide el texto de la respuesta en fragmentos, simulando una transmisión real:

```php
public function test_agent_streams_response(): void
{
    $provider = new FakeAIProvider(
        new AssistantMessage('Hola mundo')
    );

    $agent = MyAgent::make()->setAiProvider($provider);

    $handler = $agent->stream(new UserMessage('Hola'));

    $chunks = [];
    foreach ($handler->events() as $event) {
        if ($event instanceof \NeuronAI\Chat\Messages\Stream\Chunks\TextChunk) {
            $chunks[] = $event->content;
        }
    }

    // La respuesta se divide en fragmentos de 5 caracteres por defecto
    $this->assertSame(['Hello', ' worl', 'd'], $chunks);

    // El mensaje final está disponible después de consumir la transmisión
    $state = $handler->run();
    $this->assertSame('Hello world', $state->getMessage()->getContent());
}
```

Puedes cambiar el tamaño del fragmento con `setStreamChunkSize()`:

```php
$provider->setStreamChunkSize(10);
```

### Salida estructurada

Proporciona una cadena JSON que coincida con el esquema de tu clase de salida. El agente la deserializará y validará como de costumbre:

```php
public function test_agent_returns_structured_output(): void
{
    $provider = new FakeAIProvider(
        new AssistantMessage('{"name": "Alice"}')
    );

    $agent = MyAgent::make()->setAiProvider($provider);

    $user = $agent->structured(new UserMessage('Genera un usuario'), User::class);

    $this->assertInstanceOf(User::class, $user);
    $this->assertSame('Alice', $user->name);
}
```

### Llamadas a herramientas

Cuando el modelo decide llamar a una herramienta, devuelve un `ToolCallMessage`. El agente ejecuta la herramienta y vuelve al proveedor para obtener una respuesta final. Encola ambas respuestas:

```php
use NeuronAI\Chat\Messages\ToolCallMessage;

public function test_agent_uses_tools(): void
{
    $searchTool = Tool::make('search', 'Buscar en la web')
        ->addProperty(new ToolProperty('query', PropertyType::STRING, 'Consulta de búsqueda', true))
        ->setCallable(fn (string $query): string => "Resultados para: {$query}");

    $provider = new FakeAIProvider(
        // Primera llamada: el modelo solicita usar la herramienta de búsqueda
        new ToolCallMessage(null, [
            (clone $searchTool)->setCallId('call_1')->setInputs(['query' => 'frameworks PHP']),
        ]),
        // Segunda llamada: el modelo responde usando el resultado de la herramienta
        new AssistantMessage('Aquí están los principales frameworks PHP...')
    );

    $agent = MyAgent::make()
        ->setAiProvider($provider)
        ->addTool($searchTool);

    $message = $agent->chat(new UserMessage('¿Cuáles son los mejores frameworks PHP?'))->getMessage();

    $this->assertSame('Aquí están los principales frameworks PHP...', $message->getContent());
    $provider->assertCallCount(2);
}
```

### Aserciones

`FakeAIProvider` incluye aserciones integradas que puedes usar en tus pruebas:

```php
// Verifica el número total de llamadas al proveedor
$provider->assertCallCount(2);

// Verifica las llamadas por método
$provider->assertMethodCallCount('chat', 1);
$provider->assertMethodCallCount('stream', 1);

// Verifica que no se hicieron llamadas
$provider->assertNothingSent();

// Verifica el prompt del sistema
$provider->assertSystemPrompt('Eres un asistente útil.');

// Verifica que se configuraron herramientas
$provider->assertToolsConfigured(['search', 'calculator']);

// Aserción personalizada con una función de devolución de llamada
$provider->assertSent(fn (RequestRecord $record): bool =>
    $record->method === 'chat'
    && $record->messages[0]->getContent() === 'Hola'
);
```

### Inspección de solicitudes

Para verificaciones más avanzadas, accede a las solicitudes registradas en bruto:

```php
$records = $provider->getRecorded();

$records[0]->method;          // 'chat', 'stream', o 'structured'
$records[0]->messages;        // Message[] enviados al proveedor
$records[0]->systemPrompt;    // El prompt del sistema en el momento de la llamada
$records[0]->tools;           // Las herramientas configuradas en el momento de la llamada
$records[0]->structuredClass; // La clase de salida (solo llamadas estructuradas)
$records[0]->structuredSchema; // El esquema JSON (solo llamadas estructuradas)
```

## RAG

Los agentes RAG dependen de un proveedor de embeddings y de un almacén vectorial, además del proveedor de IA. Neuron proporciona `FakeEmbeddingsProvider` y `FakeVectorStore` para reemplazar ambos en las pruebas.

#### FakeEmbeddingsProvider

Genera embeddings deterministas sin llamar a ninguna API externa. Úsalo donde necesites un proveedor de embeddings:

```php
use NeuronAI\Testing\FakeEmbeddingsProvider;

$embeddings = new FakeEmbeddingsProvider();
```

#### FakeVectorStore

Devuelve documentos predeterminados de `similaritySearch()` independientemente del embedding pasado. Pasa al constructor los documentos que quieres que se devuelvan:

```php
use NeuronAI\RAG\Document;
use NeuronAI\Testing\FakeVectorStore;

$vectorStore = new FakeVectorStore([
    new Document('París es la capital de Francia.'),
    new Document('Berlín es la capital de Alemania.'),
]);
```

#### Chat RAG

```php
public function test_rag_answers_from_documents(): void
{
    $provider = new FakeAIProvider(
        new AssistantMessage('París es la capital de Francia.')
    );

    $vectorStore = new FakeVectorStore([
        new Document('Francia es un país de Europa. Su capital es París.'),
    ]);

    $rag = MyRAG::make()
        ->setAiProvider($provider);
        ->setEmbeddingsProvider(new FakeEmbeddingsProvider());
        ->setVectorStore($vectorStore);

    $message = $rag->chat(new UserMessage('¿Cuál es la capital de Francia?'))->getMessage();

    $this->assertSame('París es la capital de Francia.', $message->getContent());
    $provider->assertCallCount(1);
    $vectorStore->assertSearchCount(1);
}
```

#### Agregar documentos

Prueba que tu canalización de indexación incrusta y almacena documentos correctamente:

```php
public function test_documents_are_embedded_and_stored(): void
{
    $embeddings = new FakeEmbeddingsProvider();
    $vectorStore = new FakeVectorStore();

    $rag = MyRAG::make()
        ->setAiProvider(new FakeAIProvider());
        ->setEmbeddingsProvider($embeddings);
        ->setVectorStore($vectorStore);

    $rag->addDocuments([
        new Document('Primer documento'),
        new Document('Segundo documento'),
    ]);

    $embeddings->assertCallCount(2);
    $vectorStore->assertDocumentCount(2);
    $vectorStore->assertHasDocumentWithContent('Primer documento');
}
```

#### Aserciones de RAG

```php
// FakeEmbeddingsProvider
$embeddings->assertCallCount(2);
$embeddings->assertEmbeddedText('Algún texto específico');
$embeddings->assertNothingEmbedded();

// FakeVectorStore
$vectorStore->assertSearchCount(1);
$vectorStore->assertDocumentCount(3);
$vectorStore->assertHasDocumentWithContent('Contenido esperado');
$vectorStore->assertNothingStored();
```
