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
endSpy 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)
endTest 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 that a spy tool was called with specific arguments.
Example
Tool.Testing.assert_called(calls, %{"query" => "elixir"})
@spec assert_not_called(pid()) :: :ok
Assert that a spy tool was NOT called.
Example
Tool.Testing.assert_not_called(calls)
@spec call_count(pid()) :: non_neg_integer()
Get the count of recorded calls.
Example
assert Tool.Testing.call_count(calls) == 3
@spec clear_calls(pid()) :: :ok
Clear recorded calls from a spy tool.
Example
Tool.Testing.clear_calls(calls)
@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)
@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")
@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
@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: []})
@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)
@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"})
@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)
@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)
@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"
)
@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
})