Mock delle chiamate LLM nei test
unittest.mock, fixture di pytest e registrazione/riproduzione delle risposte degli LLM
Mock delle chiamate LLM nei test è una lezione AI Agents gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento AI Agents, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso AI Agents include 4 lezioni in totale.
Che cos'è il mocking
Il mocking sostituisce una funzione o un oggetto reale con una versione fittizia che restituisce risposte controllate. Nei test degli agenti, si simulano le chiamate API agli LLM affinché i test vengano eseguiti istantaneamente, non abbiano costi e producano risultati prevedibili.
Il modulo unittest.mock di Python è lo strumento standard per farlo.
Nozioni di base su unittest.mock.patch()
unittest.mock.patch(target) sostituisce temporaneamente l'oggetto indicato per la durata di un test. target è una stringa con notazione a punti che punta all'oggetto così come viene importato nel modulo sottoposto a test.
from unittest.mock import patch, MagicMock
# The function under test calls openai.chat.completions.create
# We patch it so no real API call is made
def ask_llm(question: str) -> str:
import openai
client = openai.OpenAI(api_key='test')
resp = client.chat.completions.create(
model='gpt-4o-mini',
messages=[{'role': 'user', 'content': question}]
)
return resp.choices[0].message.content
with patch('openai.OpenAI') as mock_client_class:
mock_instance = MagicMock()
mock_client_class.return_value = mock_instance
mock_instance.chat.completions.create.return_value = MagicMock(
choices=[MagicMock(message=MagicMock(content='Paris'))]
)
result = ask_llm('Capital of France?')
print(result) # 'Paris' — no API call madeUtilizzare patch come decoratore in pytest
Quando viene utilizzato come decoratore con pytest, @patch() inserisce il mock come parametro della funzione. Il mock viene rimosso automaticamente al termine del test.
from unittest.mock import patch, MagicMock
import pytest
# Assume agent.py contains: import openai; client = openai.OpenAI(...)
@patch('agent.openai.OpenAI')
def test_agent_calls_llm(mock_openai_class):
# Set up the mock chain
mock_client = MagicMock()
mock_openai_class.return_value = mock_client
mock_client.chat.completions.create.return_value = MagicMock(
choices=[MagicMock(message=MagicMock(content='Paris is the capital of France.'))]
)
from agent import ask_llm
result = ask_llm('What is the capital of France?')
assert 'Paris' in result
mock_client.chat.completions.create.assert_called_once()Creare una risposta mock riutilizzabile
Creare manualmente oggetti risposta mock è prolisso. Crei una funzione di supporto che costruisca oggetti mock strutturati correttamente e conformi alla struttura delle risposte dell'SDK OpenAI.
from unittest.mock import MagicMock
def make_mock_response(content: str, tool_calls: list = None) -> MagicMock:
message = MagicMock()
message.content = content
message.tool_calls = tool_calls or []
choice = MagicMock()
choice.message = message
choice.finish_reason = 'stop' if not tool_calls else 'tool_calls'
response = MagicMock()
response.choices = [choice]
response.usage = MagicMock(total_tokens=42)
return response
# Usage in tests:
# mock_create.return_value = make_mock_response('Hello!')
# mock_create.return_value = make_mock_response('', tool_calls=[...])
# --- demo ---
response = make_mock_response('The weather in Paris is 18C and sunny.')
print('content:', response.choices[0].message.content)
print('finish_reason:', response.choices[0].finish_reason)
print('total_tokens:', response.usage.total_tokens)
Simulare le chiamate ai tool nelle risposte
Quando testa la logica di chiamata ai tool di un agente, la risposta mock deve includere un campo tool_calls strutturato correttamente, affinché il codice di analisi dell'agente possa elaborarlo senza errori.
import json
from unittest.mock import MagicMock
def make_tool_call_response(tool_name: str, arguments: dict) -> MagicMock:
tool_call = MagicMock()
tool_call.id = 'call_abc123'
tool_call.type = 'function'
tool_call.function = MagicMock()
tool_call.function.name = tool_name
tool_call.function.arguments = json.dumps(arguments)
message = MagicMock()
message.content = None
message.tool_calls = [tool_call]
response = MagicMock()
response.choices = [MagicMock(message=message, finish_reason='tool_calls')]
return response
# mock.return_value = make_tool_call_response('search_web', {'query': 'Python tutorials'})
# --- demo ---
response = make_tool_call_response('search_web', {'query': 'Python tutorials'})
call = response.choices[0].message.tool_calls[0]
print('tool name:', call.function.name)
print('tool arguments:', call.function.arguments)
print('finish_reason:', response.choices[0].finish_reason)
Fixture di pytest per il mocking
Le fixture di pytest consentono di definire codice di configurazione riutilizzabile. Crei una fixture che applichi una patch al client LLM e la fornisca a ogni test che la richiede — senza ripetizioni.
import pytest
from unittest.mock import patch, MagicMock
@pytest.fixture
def mock_openai(make_mock_response):
with patch('myagent.client.chat.completions.create') as mock_create:
mock_create.return_value = MagicMock(
choices=[MagicMock(message=MagicMock(
content='Default mocked response',
tool_calls=[]
))]
)
yield mock_create
# Now any test can use it:
def test_agent_responds(mock_openai):
from myagent import agent
result = agent.run('Hello')
assert result is not None
mock_openai.assert_called_once()La fixture mocker di pytest-mock
pytest-mock fornisce una fixture mocker che semplifica l'applicazione delle patch. Pulisce automaticamente i mock e offre una sintassi più chiara rispetto a unittest.mock.patch usato direttamente.
# pip install pytest-mock
# In your test file:
def test_agent_with_mocker(mocker):
mock_create = mocker.patch('myagent.client.chat.completions.create')
mock_create.return_value = mocker.MagicMock(
choices=[mocker.MagicMock(message=mocker.MagicMock(
content='Mocked answer',
tool_calls=[]
))]
)
from myagent import agent
result = agent.run('What is 2+2?')
assert 'answer' in result.lower() or '4' in result
mock_create.assert_called_once()
# No cleanup needed — mocker handles itRegistrare e riprodurre con vcr.py
vcrpy registra le interazioni HTTP reali in un file «cassetta» alla prima esecuzione, quindi le riproduce nelle esecuzioni successive. È ideale per testare il codice che utilizza l'API HTTP direttamente invece di un SDK.
# pip install vcrpy
import vcr
import httpx
@vcr.use_cassette('fixtures/cassettes/openai_chat.yaml')
def test_with_recorded_response():
# First run: makes a real HTTP call and records it
# Subsequent runs: uses the recorded cassette (no network, no cost)
response = httpx.post(
'https://api.openai.com/v1/chat/completions',
json={'model': 'gpt-4o-mini', 'messages': [{'role': 'user', 'content': 'Hello'}]},
headers={'Authorization': 'Bearer YOUR_KEY'}
)
data = response.json()
assert data['choices'][0]['message']['content'] is not NoneVerificare che il mock sia stato chiamato correttamente
Dopo un test, verifichi che il mock sia stato chiamato con gli argomenti corretti. In questo modo è possibile rilevare i bug in cui l'agente invia il modello sbagliato, omette parametri o usa messaggi non corretti.
from unittest.mock import patch, MagicMock, call
@patch('myagent.client.chat.completions.create')
def test_agent_sends_correct_model(mock_create):
mock_create.return_value = MagicMock(
choices=[MagicMock(message=MagicMock(content='ok', tool_calls=[]))]
)
from myagent import agent
agent.run('Hello')
# Verify the mock was called with correct arguments
mock_create.assert_called_once()
call_kwargs = mock_create.call_args.kwargs
assert call_kwargs['model'] == 'gpt-4o-mini'
assert len(call_kwargs['messages']) >= 1
assert call_kwargs['messages'][0]['role'] == 'system'Simulare gli errori API nei test
Verifichi come l'agente gestisce gli errori degli LLM configurando il mock affinché sollevi eccezioni. In questo modo può verificare la gestione degli errori e la logica dei tentativi senza provocare errori API reali.
from unittest.mock import patch
import openai
@patch('myagent.client.chat.completions.create')
def test_agent_handles_rate_limit(mock_create):
# Simulate a rate limit error
mock_create.side_effect = openai.RateLimitError(
message='Rate limit exceeded',
response=None,
body=None
)
from myagent import agent
result = agent.run('Hello')
# Agent should handle this gracefully
assert result['error'] == 'rate_limit'
# or
assert result['retry_after'] is not NoneOrganizzare le fixture mock in conftest.py
Inserisca le fixture condivise in conftest.py nella directory principale dei test. pytest rileva automaticamente questo file e rende disponibili le fixture a tutti i file di test senza doverle importare.
# tests/conftest.py
import pytest
from unittest.mock import patch, MagicMock
@pytest.fixture(autouse=False)
def mock_llm():
with patch('myagent.client.chat.completions.create') as mock_create:
mock_create.return_value = MagicMock(
choices=[MagicMock(message=MagicMock(
content='Test response',
tool_calls=[]
))]
)
yield mock_create
@pytest.fixture
def mock_search_tool():
with patch('myagent.tools.search_web') as mock_search:
mock_search.return_value = [{'title': 'Test', 'url': 'https://example.com'}]
yield mock_searchVerifica delle conoscenze: simulare le chiamate LLM
Verifichi la propria comprensione delle tecniche di mocking per i test degli agenti.
Riepilogo: simulare le chiamate LLM nei test
Ora dispone degli strumenti per scrivere test unitari degli agenti rapidi e affidabili:
- Utilizzi
unittest.mock.patch()per sostituire i client LLM con dei mock - Crei helper riutilizzabili per le risposte mock, conformi alla struttura delle risposte dell'SDK
- Simuli le chiamate ai tool con campi
tool_callsstrutturati correttamente - Utilizzi le fixture di pytest e
conftest.pyper condividere i mock tra i test - Utilizzi
pytest-mockper una sintassi più chiara - Utilizzi
vcrpyper registrare e riprodurre le interazioni HTTP reali
I mock sono la base di una suite di test per agenti rapida e facile da mantenere.
Domande Frequenti
La lezione «Mock delle chiamate LLM nei test» è gratuita?
Sì — il testo completo di «Mock delle chiamate LLM nei test» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso AI Agents, passa a CoddyKit PRO. Il corso AI Agents include 4 lezioni in totale.
Cosa imparerò in «Mock delle chiamate LLM nei test»?
unittest.mock, fixture di pytest e registrazione/riproduzione delle risposte degli LLM Eserciti AI Agents con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare AI Agents?
Non è richiesta alcuna esperienza precedente. AI Agents su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.
Quanto tempo richiede la lezione «Mock delle chiamate LLM nei test»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione AI Agents?
Sì. Ogni lezione AI Agents include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Perché testare gli agenti è diverso
- Mock delle chiamate LLM nei test
- Test degli agenti basati su asserzioni
- Test di integrazione per le pipeline degli agenti