AI Agents · Lekcja

Mockowanie wywołań LLM w testach

unittest.mock, fixtures pytest oraz nagrywanie i odtwarzanie odpowiedzi LLM.

Lekcja 2 z 413 kroki

Mockowanie wywołań LLM w testach to bezpłatna lekcja AI Agents na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej AI Agents, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs AI Agents zawiera 4 lekcji w sumie.

Czym jest mockowanie?

Mockowanie zastępuje rzeczywistą funkcję lub obiekt fikcyjną wersją, która zwraca kontrolowane odpowiedzi. W testach agentów mockujemy wywołania API LLM, dzięki czemu testy uruchamiają się natychmiast, nic nie kosztują i dają przewidywalne wyniki.

Moduł Pythona unittest.mock jest do tego standardowym narzędziem.

Podstawy unittest.mock.patch()

unittest.mock.patch(target) tymczasowo zastępuje nazwany obiekt na czas trwania testu. target to ciąg znaków w notacji kropkowej, wskazujący obiekt w takiej postaci, w jakiej jest importowany w testowanym module.

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 made

Używanie patch jako dekoratora w pytest

Użyte jako dekorator w pytest, @patch() wstrzykuje mock jako parametr funkcji. Mock jest automatycznie usuwany po zakończeniu testu.

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

Tworzenie wielokrotnego użytku mockowanej odpowiedzi

Ręczne tworzenie obiektów mock odpowiedzi jest rozwlekłe. Należy utworzyć funkcję pomocniczą, która buduje poprawnie ustrukturyzowane obiekty mock zgodne ze strukturą odpowiedzi OpenAI SDK.

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)

Mockowanie wywołań narzędzi w odpowiedziach

Podczas testowania logiki wywoływania narzędzi przez agenta mockowana odpowiedź musi zawierać odpowiednio ustrukturyzowane pole tool_calls, aby kod parsujący agenta mógł ją poprawnie przetworzyć.

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'y pytest do mockowania

Fixture'y pytest pozwalają definiować wielokrotnie używany kod konfiguracyjny. Należy utworzyć fixture, która patchuje klienta LLM i udostępnia go każdemu testowi, który jej potrzebuje — bez powtarzania kodu.

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

Fixture mocker z pytest-mock

pytest-mock udostępnia fixture mocker, która upraszcza patchowanie. Automatycznie sprząta mocki i oferuje czytelniejszą składnię niż bezpośrednie użycie unittest.mock.patch.

# 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 it

Nagrywanie i odtwarzanie za pomocą vcr.py

vcrpy podczas pierwszego uruchomienia rejestruje rzeczywiste interakcje HTTP w pliku typu „cassette”, a następnie odtwarza je przy kolejnych uruchomieniach. Jest to idealne rozwiązanie do testowania kodu korzystającego z bezpośredniego API HTTP zamiast 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 None

Sprawdzanie poprawnego wywołania mocka

Po teście należy sprawdzić, czy mock został wywołany z właściwymi argumentami. Pozwala to wykryć błędy, w których agent wysyła niewłaściwy model, pomija parametry lub przekazuje niepoprawne wiadomości.

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'

Symulowanie błędów API w testach

Należy sprawdzić, jak agent obsługuje awarie LLM, konfigurując mock tak, aby zgłaszał wyjątki. Pozwala to zweryfikować obsługę błędów i logikę ponawiania prób bez powodowania rzeczywistych awarii API.

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 None

Organizowanie fixture'ów mocków w conftest.py

Należy umieścić współdzielone fixture'y w pliku conftest.py w katalogu głównym testów. pytest automatycznie wykrywa ten plik i udostępnia fixture'y wszystkim plikom testów bez konieczności ich importowania.

# 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_search

Sprawdzenie wiedzy: Mockowanie wywołań LLM

Sprawdź swoją wiedzę na temat technik mockowania stosowanych w testach agentów.

Podsumowanie: Mockowanie wywołań LLM w testach

Można już pisać szybkie i niezawodne testy jednostkowe agentów:

  • Należy używać unittest.mock.patch() do zastępowania klientów LLM mockami
  • Należy budować wielokrotnego użytku pomocniki tworzące mockowane odpowiedzi zgodne ze strukturą odpowiedzi SDK
  • Należy mockować wywołania narzędzi za pomocą odpowiednio ustrukturyzowanych pól tool_calls
  • Należy używać fixture'ów pytest i pliku conftest.py do współdzielenia mocków między testami
  • Należy używać pytest-mock dla czytelniejszej składni
  • Należy używać vcrpy do rejestrowania i odtwarzania rzeczywistych interakcji HTTP

Mocki są podstawą szybkiego i łatwego w utrzymaniu zestawu testów agenta.

Bezpłatny start

Ucz się AI Agents dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
60
Lekcje
239

Często zadawane pytania

Czy lekcja „Mockowanie wywołań LLM w testach” jest bezpłatna?

Tak — pełny tekst „Mockowanie wywołań LLM w testach” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu AI Agents, przejdź na CoddyKit PRO. Kurs AI Agents zawiera 4 lekcji w sumie.

Co nauczysz się w „Mockowanie wywołań LLM w testach”?

unittest.mock, fixtures pytest oraz nagrywanie i odtwarzanie odpowiedzi LLM. Ćwiczysz AI Agents z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć AI Agents?

Nie wymagamy żadnego doświadczenia. AI Agents w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Mockowanie wywołań LLM w testach”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji AI Agents?

Tak. Każda lekcja AI Agents zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Dlaczego testowanie agentów jest inne
  2. Mockowanie wywołań LLM w testach
  3. Testowanie agentów oparte na asercjach
  4. Testy integracyjne potoków agentów
← Powrót do AI Agents