# `Nous.Tool.Testing`
[🔗](https://github.com/nyo16/nous/blob/v0.17.1/lib/nous/tool/testing.ex#L1)

Test helpers for tools.

Provides utilities for creating mock tools, spy tools, and test contexts
to make tool testing easier.

## Mock Tools

Create tools that return fixed results:

    test "agent handles search results" do
      mock_search = Tool.Testing.mock_tool("search", %{results: ["a", "b"]})

      agent = Agent.new("test:model", tools: [mock_search])
      # Agent will receive mock results when it calls the search tool
    end

## Spy Tools

Create tools that record all calls for verification:

    test "agent uses search tool correctly" do
      {spy_tool, calls} = Tool.Testing.spy_tool("search", result: %{found: true})

      agent = Agent.new("test:model", tools: [spy_tool])
      Agent.run(agent, "Search for elixir")

      recorded = Tool.Testing.get_calls(calls)
      assert length(recorded) == 1
      assert {_ctx, %{"query" => "elixir"}} = hd(recorded)
    end

## Test Contexts

Create contexts with test dependencies:

    test "database tool queries correctly" do
      mock_db = %{query: fn sql -> [%{id: 1}] end}
      ctx = Tool.Testing.test_context(%{database: mock_db})

      assert {:ok, [%{id: 1}]} = MyTools.DatabaseTool.execute(ctx, %{"sql" => "SELECT *"})
    end

# `assert_called`

```elixir
@spec assert_called(pid(), map()) :: :ok
```

Assert that a spy tool was called with specific arguments.

## Example

    Tool.Testing.assert_called(calls, %{"query" => "elixir"})

# `assert_not_called`

```elixir
@spec assert_not_called(pid()) :: :ok
```

Assert that a spy tool was NOT called.

## Example

    Tool.Testing.assert_not_called(calls)

# `call_count`

```elixir
@spec call_count(pid()) :: non_neg_integer()
```

Get the count of recorded calls.

## Example

    assert Tool.Testing.call_count(calls) == 3

# `clear_calls`

```elixir
@spec clear_calls(pid()) :: :ok
```

Clear recorded calls from a spy tool.

## Example

    Tool.Testing.clear_calls(calls)

# `error_tool`

```elixir
@spec error_tool(String.t(), term(), keyword()) :: Nous.Tool.t()
```

Create a tool that returns an error tuple.

## Example

    error_tool = Tool.Testing.error_tool("api", :connection_refused)

# `failing_tool`

```elixir
@spec failing_tool(
  String.t(),
  keyword()
) :: Nous.Tool.t()
```

Create a failing tool that raises an error.

Useful for testing error handling.

## Options

- `:error` - The error to raise (default: RuntimeError)
- `:message` - Error message (default: "Tool failed")

## Example

    failing = Tool.Testing.failing_tool("broken", message: "Connection timeout")

# `get_calls`

```elixir
@spec get_calls(pid()) :: [{Nous.RunContext.t(), map()}]
```

Get recorded calls from a spy tool.

Returns calls in chronological order (oldest first).

## Example

    {spy, calls} = Tool.Testing.spy_tool("search")
    # ... use spy ...
    recorded = Tool.Testing.get_calls(calls)
    assert [{ctx1, args1}, {ctx2, args2}] = recorded

# `mock_tool`

```elixir
@spec mock_tool(String.t(), any(), keyword()) :: Nous.Tool.t()
```

Create a mock tool that returns a fixed result.

## Options

- `:description` - Tool description (default: "Mock tool for testing")
- `:parameters` - Tool parameters schema (default: empty object)

## Example

    mock = Tool.Testing.mock_tool("search", %{results: []})

# `mock_tool_fn`

```elixir
@spec mock_tool_fn(String.t(), (Nous.RunContext.t(), map() -&gt; any()), keyword()) ::
  Nous.Tool.t()
```

Create a mock tool that returns different results based on a function.

The function receives (ctx, args) and should return the result.

## Example

    mock = Tool.Testing.mock_tool_fn("calculate", fn _ctx, %{"op" => "add", "a" => a, "b" => b} ->
      a + b
    end)

# `slow_tool`

```elixir
@spec slow_tool(String.t(), non_neg_integer(), any(), keyword()) :: Nous.Tool.t()
```

Create a tool that sleeps for a duration before returning.

Useful for testing timeouts.

## Example

    slow_tool = Tool.Testing.slow_tool("api_call", 5000, %{result: "ok"})

# `spy_tool`

```elixir
@spec spy_tool(
  String.t(),
  keyword()
) :: {Nous.Tool.t(), pid()}
```

Create a spy tool that records all calls.

Returns a tuple of {tool, calls_agent} where calls_agent is an Agent process
that stores all calls. Use `get_calls/1` to retrieve recorded calls.

## Options

- `:result` - Result to return from the tool (default: %{success: true})
- `:description` - Tool description
- `:parameters` - Tool parameters schema

## Example

    {spy, calls} = Tool.Testing.spy_tool("search")
    # ... use spy in agent ...
    recorded = Tool.Testing.get_calls(calls)

# `spy_tool_fn`

```elixir
@spec spy_tool_fn(String.t(), (Nous.RunContext.t(), map() -&gt; any()), keyword()) ::
  {Nous.Tool.t(), pid()}
```

Create a spy tool that can return different results and track calls.

Similar to `spy_tool/2` but accepts a function to determine the result.

## Example

    {spy, calls} = Tool.Testing.spy_tool_fn("search", fn ctx, args ->
      # Custom logic to determine result
      %{query: args["query"], found: true}
    end)

# `test_agent_context`

```elixir
@spec test_agent_context(
  map(),
  keyword()
) :: Nous.Agent.Context.t()
```

Create an Agent.Context with test configuration.

## Options

- `:messages` - Initial messages
- `:system_prompt` - System prompt
- `:max_iterations` - Max iterations (default: 10)

## Example

    ctx = Tool.Testing.test_agent_context(%{database: mock_db},
      system_prompt: "Be helpful"
    )

# `test_context`

```elixir
@spec test_context(
  map(),
  keyword()
) :: Nous.RunContext.t()
```

Create a RunContext with test dependencies.

## Options

- `:retry` - Retry count (default: 0)
- `:usage` - Usage struct (default: empty)

## Example

    ctx = Tool.Testing.test_context(%{
      database: mock_db,
      http_client: mock_http
    })

---

*Consult [api-reference.md](api-reference.md) for complete listing*
