Skip to content

Testing

The SDK includes a fake client implementation that lets you test your code without making real API calls. You set up fake responses, run your code, and then assert that the right requests were sent.

Setting up the fake client

Replace the real Anthropic\Client with Anthropic\Testing\ClientFake in your tests. Pass an array of fake responses that will be returned in order:

use Anthropic\Testing\ClientFake;
use Anthropic\Responses\Messages\CreateResponse;

$client = new ClientFake([
    CreateResponse::fake([
        'content' => [
            ['type' => 'text', 'text' => 'Hello! How can I help?'],
        ],
    ]),
]);

$response = $client->messages()->create([
    'model' => 'claude-sonnet-4-6',
    'max_tokens' => 1024,
    'messages' => [
        ['role' => 'user', 'content' => 'Hello!'],
    ],
]);

$response->content[0]->text; // 'Hello! How can I help?'

The fake() method on response classes creates a response object with sensible defaults. Pass an array to override only the fields your test cares about. Overrides are merged recursively by default, so you can override a single nested field without replacing the entire parent array.

Available fake responses

Every response class has a fake() method:

Response class Resource
Messages\CreateResponse::fake() Messages create
Messages\CountTokensResponse::fake() Token counting
Messages\CreateStreamedResponse::fake() Messages streaming
Completions\CreateResponse::fake() Completions create
Completions\CreateStreamedResponse::fake() Completions streaming
Models\ListResponse::fake() Models list
Models\RetrieveResponse::fake() Models retrieve
Batches\BatchResponse::fake() Batch create/retrieve/cancel
Batches\BatchListResponse::fake() Batch list
Batches\BatchResultResponse::fake() Batch results
Batches\DeletedBatchResponse::fake() Batch delete
Files\FileResponse::fake() Files upload/retrieve metadata
Files\FileListResponse::fake() Files list
Files\DeletedFileResponse::fake() Files delete

Overriding response fields

Only override what matters for your test. Everything else gets default values:

use Anthropic\Responses\Completions\CreateResponse;

$client = new ClientFake([
    CreateResponse::fake([
        'completion' => 'PHP is awesome!',
    ]),
]);

$response = $client->completions()->create([
    'model' => 'claude-2.1',
    'prompt' => '\n\nHuman: PHP is \n\nAssistant:',
    'max_tokens_to_sample' => 100,
]);

expect($response->completion)->toBe('PHP is awesome!');

Fake streamed responses

For streaming, pass a file resource containing the SSE event data:

use Anthropic\Testing\ClientFake;
use Anthropic\Responses\Messages\CreateStreamedResponse;

$client = new ClientFake([
    CreateStreamedResponse::fake(fopen('tests/fixtures/stream.txt', 'r')),
]);

$stream = $client->messages()->createStreamed([
    'model' => 'claude-sonnet-4-6',
    'max_tokens' => 1024,
    'messages' => [
        ['role' => 'user', 'content' => 'Hello!'],
    ],
]);

expect($stream->getIterator()->current())
    ->type->toBe('message_start');

You can also call fake() with no arguments. It uses a built-in default fixture file:

$client = new ClientFake([
    CreateStreamedResponse::fake(),
]);

Building a stream from text chunks

For tests that need predictable streamed text (without a fixture file), you can build a fake stream in memory. This helper takes an array of text parts and returns a resource with the full SSE event sequence:

function fakeStream(array $parts)
{
    $events = [];

    $events[] = [
        'type' => 'message_start',
        'message' => [
            'id' => 'msg_test',
            'type' => 'message',
            'role' => 'assistant',
            'model' => 'claude-sonnet-4-6',
            'content' => [],
            'stop_reason' => null,
            'stop_sequence' => null,
            'usage' => ['input_tokens' => 10, 'output_tokens' => 1],
        ],
    ];

    foreach ($parts as $part) {
        $events[] = [
            'type' => 'content_block_delta',
            'index' => 0,
            'delta' => ['type' => 'text_delta', 'text' => $part],
        ];
    }

    $events[] = [
        'type' => 'message_delta',
        'delta' => ['stop_reason' => 'end_turn', 'stop_sequence' => null],
        'usage' => ['output_tokens' => 12],
    ];

    $events[] = ['type' => 'message_stop'];

    $body = implode("\n\n", array_map(
        fn (array $event): string => "event: {$event['type']}\ndata: " . json_encode($event),
        $events,
    ));

    $handle = fopen('php://memory', 'r+');
    fwrite($handle, $body);
    rewind($handle);

    return $handle;
}

// Use it in a test
$client = new ClientFake([
    CreateStreamedResponse::fake(fakeStream(['Hello', ', ', 'world!'])),
]);

The stream emits message_start, three content_block_delta events with the text parts, message_delta, and message_stop. This is enough to test code that reads incremental text without needing an external fixture file.

Testing errors

To test error handling, pass an exception as a fake response. It will be thrown when the matching request is made:

use Anthropic\Testing\ClientFake;
use Anthropic\Exceptions\ErrorException;

$client = new ClientFake([
    new ErrorException([
        'message' => 'Overloaded',
        'type' => 'overloaded_error',
    ], 529),
]);

// This will throw the ErrorException
$client->messages()->create([
    'model' => 'claude-sonnet-4-6',
    'max_tokens' => 1024,
    'messages' => [
        ['role' => 'user', 'content' => 'Hello'],
    ],
]);

Assertions

After running your code, use assertions to verify which requests were sent.

assertSent

Check that a request was made to a specific resource:

use Anthropic\Resources\Messages;

// Assert any request was sent to the Messages resource
$client->assertSent(Messages::class);

// Assert with a callback for specific parameters
$client->assertSent(Messages::class, function (string $method, array $parameters): bool {
    return $method === 'create'
        && $parameters['model'] === 'claude-sonnet-4-6';
});

// Assert a specific number of requests
$client->assertSent(Messages::class, 2);

assertNotSent

Check that no request was made to a resource:

use Anthropic\Resources\Completions;
use Anthropic\Resources\Messages;

$client->assertNotSent(Completions::class);

// With a callback: assert no request matched this condition
$client->assertNotSent(Messages::class, function (string $method, array $parameters): bool {
    return $parameters['model'] === 'claude-2.1';
});

assertNothingSent

Check that no requests were sent at all:

$client->assertNothingSent();

Resource-level assertions

You can also assert directly on a resource:

$client->messages()->assertSent(function (string $method, array $parameters): bool {
    return $method === 'create'
        && $parameters['max_tokens'] === 1024;
});

$client->completions()->assertNotSent();

Adding responses dynamically

If you need to add responses after creating the fake client (for example, in a loop or conditional logic):

$client = new ClientFake();

$client->addResponses([
    CreateResponse::fake(['completion' => 'First response']),
    CreateResponse::fake(['completion' => 'Second response']),
]);

Multiple sequential requests

Fake responses are consumed in order. The first request gets the first response, the second request gets the second, and so on:

$client = new ClientFake([
    CreateResponse::fake(['content' => [['type' => 'text', 'text' => 'First']]]),
    CreateResponse::fake(['content' => [['type' => 'text', 'text' => 'Second']]]),
]);

$first = $client->messages()->create([...]); // Returns "First"
$second = $client->messages()->create([...]); // Returns "Second"

If you make more requests than you have fake responses, the client will throw an error.

Scroll to top