# Testing your agent

{% hint style="success" %}
This is the official documentation of the `@forestadmin/agent` Node.js agent.
{% endhint %}

## Testing Your Agent

Test your Forest Admin customizations locally without connecting to Forest Admin servers.

### Installation

```bash
npm install --save-dev @forestadmin/agent-testing
```

### Quick Start

The testing library provides two main functions:

* **`createForestServerSandbox(port)`** - Starts a local mock server that simulates Forest Admin servers.
* **`createAgentTestClient(options)`** - Creates a test client to interact with your agent.

```typescript
import {
  createAgentTestClient,
  createForestServerSandbox,
} from '@forestadmin/agent-testing';

describe('My Agent', () => {
  let sandbox, client;

  beforeAll(async () => {
    // Start mock server
    sandbox = await createForestServerSandbox(3001);
    // Connect test client
    client = await createAgentTestClient({
      serverUrl: 'http://localhost:3001',
      agentUrl: 'http://localhost:3310',
      agentSchemaPath: './.forestadmin-schema.json',
      agentForestEnvSecret: process.env.FOREST_ENV_SECRET,
      agentForestAuthSecret: process.env.FOREST_AUTH_SECRET,
    });
  });

  afterAll(async () => {
    await sandbox?.stop();
  });

  it('should list users', async () => {
    const users = await client.collection('users').list();
    expect(users.length).toBeGreaterThan(0);
  });

  it('should execute action', async () => {
    const action = await client
      .collection('orders')
      .action('Apply discount', { recordId: 1 });
    await action.getFieldNumber('discount').fill(10);
    const result = await action.execute();
    expect(result.success).toBe(true);
  });
});
```

### Collections

Test CRUD operations and hooks.

#### List

Test that filters return the correct records.

```typescript
// Fetch admins
const admins = await client.collection('users').list({
  filters: { field: 'role', operator: 'Equal', value: 'admin' },
});
// Check all have role 'admin'
expect(admins.every(u => u.role === 'admin')).toBe(true);
```

#### Create

Test that a record is correctly created.

```typescript
// Create user
const user = await client.collection('users').create({ email: 'john@example.com' });
// Fetch to verify
const [created] = await client.collection('users').list({
  filters: { field: 'id', operator: 'Equal', value: user.id },
});
// Check record exists
expect(created.email).toBe('john@example.com');
```

#### Update

Test that a record is correctly updated.

```typescript
// Update email
await client.collection('users').update(userId, { email: 'new@example.com' });
// Fetch to verify
const [user] = await client.collection('users').list({
  filters: { field: 'id', operator: 'Equal', value: userId },
});
// Check new value
expect(user.email).toBe('new@example.com');
```

#### Delete

Test that a record is correctly deleted.

```typescript
// Delete user
await client.collection('users').delete(userId);
// Fetch to verify
const users = await client.collection('users').list({
  filters: { field: 'id', operator: 'Equal', value: userId },
});
// Check record is gone
expect(users).toHaveLength(0);
```

### Actions

Test smart actions and form behavior.

```typescript
// Get action
const action = await client.collection('orders').action('Refund', { recordId: 1 });
// Fill form
await action.getFieldNumber('amount').fill(100);
await action.getFieldString('reason').fill('Customer request');
// Execute
const result = await action.execute();
// Check success
expect(result.success).toBe(true);
```

#### Dynamic forms

Test fields that appear based on other field values.

```typescript
const action = await client.collection('orders').action('Refund', { recordId: 1 });
// Check field is hidden
expect(action.doesFieldExist('manager_approval')).toBe(false);
// Fill amount > threshold
await action.getFieldNumber('amount').fill(500);
// Check field now appears
expect(action.doesFieldExist('manager_approval')).toBe(true);
```

#### Field properties

Test field validation rules.

```typescript
const action = await client.collection('users').action('Update', { recordId: 1 });
// Check required
expect(action.getFieldString('email').isRequired()).toBe(true);
// Check read-only
expect(action.getFieldNumber('age').isReadOnly()).toBe(false);
```

### Computed Fields

Test custom computed fields.

```typescript
// Fetch user
const [user] = await client.collection('users').list<{ fullName: string }>();
// Check computed value
expect(user.fullName).toBe('John Doe');
```

### Segments

Test custom segments.

```typescript
// Fetch from segment
const minors = await client.collection('users').segment('minors').list();
// Check filter works
expect(minors.every(u => u.age < 18)).toBe(true);
```

### Charts

Test dashboard charts.

```typescript
// Fetch value chart
const chart = await client.valueChart('totalRevenue');
// Check returns data
expect(chart.countCurrent).toBeGreaterThan(0);

// Fetch distribution chart
const distribution = await client.distributionChart('ordersByStatus');
// Check contains expected key
expect(distribution).toContainEqual({ key: 'pending', value: expect.any(Number) });
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.forestadmin.com/developer-guide-agents-nodejs/testing/testing.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
