Nous.Tool.Testing (nous v0.13.3)

View Source

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

Summary

Functions

Assert that a spy tool was called with specific arguments.

Assert that a spy tool was NOT called.

Get the count of recorded calls.

Clear recorded calls from a spy tool.

Create a tool that returns an error tuple.

Create a failing tool that raises an error.

Get recorded calls from a spy tool.

Create a mock tool that returns a fixed result.

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

Create a tool that sleeps for a duration before returning.

Create a spy tool that records all calls.

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

Create an Agent.Context with test configuration.

Create a RunContext with test dependencies.

Functions

assert_called(agent, expected_args)

@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(agent)

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

Assert that a spy tool was NOT called.

Example

Tool.Testing.assert_not_called(calls)

call_count(agent)

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

Get the count of recorded calls.

Example

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

clear_calls(agent)

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

Clear recorded calls from a spy tool.

Example

Tool.Testing.clear_calls(calls)

error_tool(name, error_reason, opts \\ [])

@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(name, opts \\ [])

@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(agent)

@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(name, result, opts \\ [])

@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(name, result_fn, opts \\ [])

@spec mock_tool_fn(String.t(), (Nous.RunContext.t(), map() -> 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(name, sleep_ms, result, opts \\ [])

@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(name, opts \\ [])

@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(name, result_fn, opts \\ [])

@spec spy_tool_fn(String.t(), (Nous.RunContext.t(), map() -> 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(deps \\ %{}, opts \\ [])

@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(deps \\ %{}, opts \\ [])

@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
})