FromZero2AI
CoursesPlaygroundBlog
Support on Ko-fi
AI Agent Frameworks • Module C: Production AgentsLesson 7: Testing & Debugging AI Agents
PreviousNext

Lesson 7: Testing & Debugging AI Agents

Mock external tools for deterministic testing, trace agent execution step-by-step with LangSmith, and write integration tests that verify the agent reaches the correct goal state.

Built with AI for beginners. Free forever.

Support on Ko-fi•About•Blog•Privacy Policy•Terms of Service

Testing agents is harder than testing functions — the path to a correct answer matters, not just the answer. An agent that gets the right result via an accidental sequence of tool calls is not a reliable agent.

Three Testing Strategies

Replace real tools and LLMs with deterministic mocks in tests. Fast, free, and reproducible — test agent logic without actually calling APIs.

import pytest
from unittest.mock import MagicMock, patch
from langgraph.graph import StateGraph

# ── Mock the LLM ──────────────────────────────────────────────
def make_mock_llm(responses: list[str]):
    """Returns a mock LLM that cycles through pre-defined responses."""
    mock = MagicMock()
    mock.invoke.side_effect = [
        MagicMock(content=r, tool_calls=[]) for r in responses
    ]
    return mock


# ── Mock tools ────────────────────────────────────────────────
def mock_search_tool(query: str) -> str:
    """Deterministic mock — always returns the same search result."""
    return "Search result for: " + query


# ── Test agent behavior ───────────────────────────────────────
def test_agent_completes_without_tools():
    """Test that agent terminates when LLM doesn't call tools."""
    mock_llm = make_mock_llm(["Here is the final answer: Paris"])

    with patch("myapp.agent.model", mock_llm):
        result = run_agent("What is the capital of France?")

    assert "Paris" in result["messages"][-1].content
    assert mock_llm.invoke.call_count == 1   # Called exactly once


def test_agent_uses_search_tool():
    """Test that agent correctly calls search and incorporates result."""
    tool_response = MagicMock(content="", tool_calls=[{
        "id": "call_001",
        "name": "web_search",
        "args": {"query": "population of Tokyo"},
    }])
    final_response = MagicMock(content="Tokyo has 13.96 million people.", tool_calls=[])
    mock_llm = make_mock_llm([])
    mock_llm.invoke.side_effect = [tool_response, final_response]

    with patch("myapp.agent.model", mock_llm), \
         patch("myapp.agent.web_search", mock_search_tool):
        result = run_agent("What is the population of Tokyo?")

    assert mock_llm.invoke.call_count == 2
    assert "13.96 million" in result["messages"][-1].content

Agent Test Pyramid

LevelTestsCountRun on
Unit (mocked)State transitions, routing logic, tool input validation~30Every commit
Integration (real tools)Tool calls work, API responses handled correctly~10Every PR
E2E eval (real LLM)Agent achieves goal on golden dataset~20 tasksBefore release
AdversarialPrompt injection, hallucination, loop-breaking inputs~10Before release

You are now ready for the capstone: building a multi-agent research team that uses LangGraph, CrewAI, human-in-the-loop approval, and a full test suite.