Testing¶
Test an agent without an API key or network. FakeModel replaces the model and returns prepared replies in order.
A test¶
from pathlib import Path
from alpineagents import Agent, State, tool
from alpineagents.testing import FakeModel, tool_call
@tool
def read_file(path: str) -> str:
"""Read a file"""
return Path(path).read_text()
agent = Agent(model="claude-sonnet-5", tools=[read_file])
def test_reads_the_file_then_answers(tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path)
Path("main.py").write_text("x = 1 +\n")
fake = FakeModel([
tool_call("read_file", path="main.py"),
"Line 1 has a syntax error",
])
state = State("Find the bug in main.py")
agent.copy(model=fake, reporter=None).run(state)
assert state.answer == "Line 1 has a syntax error"
assert state.stopped_by == "is_answered"
assert fake.remaining == 0
FakeModelgets two replies: a call toread_file, then the answer.agent.copy(model=fake, reporter=None)is a new Agent with the same settings, except the fake model and no terminal output.- The assertions check the answer, why the run stopped, and that every reply was used.
Replies FakeModel accepts¶
Each model request takes the next item: every think, every ask attempt and every compact (also from
compact_if_full). When the items run out, the request raises RuntimeError.
| Item | The reply |
|---|---|
"text" |
A text reply without tool calls |
tool_call("name", arg=value) |
A reply with one tool call |
| A list of text and tool calls | A reply with those parts in order, for example ["Let me check.", tool_call(...)] |
An exception instance, such as RateLimitError("slow down") |
The request raises it |
A Reply object |
Used as is |
A function that takes the Request |
Its return value, read by the rules above |
fake.requests holds every request the model received. Use it to check what the model saw:
assert [t.name for t in fake.requests[0].tools] == ["read_file"]
Questions to the person¶
FakeHuman answers ask_human with prepared answers in order. agent is the Agent from
Ask before a tool runs:
from alpineagents.testing import FakeHuman, FakeModel, tool_call
fake = FakeModel([tool_call("write_file", path="a.md", content="x"), "Skipped"])
human = FakeHuman(["no"])
agent.copy(model=fake, human=human, reporter=None).run("Write a.md")
assert human.remaining == 0
An answer that does not fit the question's returns type is skipped, and the next one is used.
What to assert¶
| To check | Assert on |
|---|---|
| The result | state.answer |
| Why the run stopped | state.stopped_by |
| How many turns | state.turn |
| What happened, in order | state.history, for example [h.kind for h in state.history]. The kinds are listed in the Data types API |
| What the model saw | fake.requests |