Saloon makes it easy to fake API integrations in your tests. In your tests, you will need to create an instance of MockClient
. The MockClient
accepts an array of MockResponses
which when used on a request, will respond with a fake response without actually sending a real request to the web. This helps speed up tests massively and can help you test your application for different API response scenarios, like a 404 error or 500 error.
Request mocking starts with a MockClient
. This class can be applied directly to a connector instance to be used across all requests, or it can be applied on a per-request basis.
{% tabs %} {% tab title="All Requests (Connector)" %}
<?php
use Saloon\Http\Faking\MockClient;
use Saloon\Http\Faking\MockResponse;
$mockClient = new MockClient([
MockResponse::make(['name' => 'Sam'], 200),
MockResponse::make(['name' => 'Alex'], 200),
MockResponse::make(['error' => 'Server Unavailable'], 500),
]);
$forge = new ForgeConnector;
$forge->withMockClient($mockClient);
// All requests sent with the $forge instance will use the Mock Client
{% endtab %}
{% tab title="Individual Request" %}
<?php
use Saloon\Http\Faking\MockClient;
use Saloon\Http\Faking\MockResponse;
$mockClient = new MockClient([
MockResponse::make(['name' => 'Sam'], 200),
MockResponse::make(['name' => 'Alex'], 200),
MockResponse::make(['error' => 'Server Unavailable'], 500),
]);
$forge = new ForgeConnector;
$request = new GetAllServersRequest;
// Send a request with a MockClient
$forge->send($request, $mockClient);
{% endtab %} {% endtabs %}
If you are using Laravel, as well as the Saloon Laravel helper library, you don't have to use withMockClient
on every instance of your connector. You may use the Saloon
facade fake
method to define your mock responses. This is a built-in global MockClient that when used will be applied to all Saloon requests sent in your application.
<?php
use Saloon\Laravel\Saloon;
Saloon::fake([
MockResponse::make(['name' => 'Sam'], 200),
MockResponse::make(['name' => 'Alex'], 200),
MockResponse::make(['error' => 'Server Unavailable'], 500),
]);
// All requests sent with the $forge instance will use the global MockClient
$forge = new ForgeConnector;
$forge->send(new GetUserRequest) // Will return with `['name' => 'Sam']` and status `200`
The MockResponse class is used to create fake responses Saloon understands. It can accept a body, status, and headers. These properties will be populated in the fake response. The response body accepts an array for a JSON body or plain strings to simulate other responses, like XML.
use Saloon\Http\Faking\MockResponse;
MockResponse::make(['name' => 'Sam'], 200, ['Content-Type' => 'application/json']);
Basic sequence testing allows you to define a number of fake responses. When your application uses Saloon, it will pull out the next response in the sequence, removing it from the sequence. Each response can only be consumed once.
use Saloon\Http\Faking\MockClient;
use Saloon\Http\Faking\MockResponse;
$mockClient = new MockClient([
MockResponse::make(['name' => 'Sam'], 200),
MockResponse::make(['name' => 'Alex'], 200),
MockResponse::make(['error' => 'Server Unavailable'], 500),
]);
$forge = new ForgeConnector;
$forge->withMockClient($mockClient);
$otherService = new OtherServiceConnector;
$otherService->withMockClient($mockClient);
$forge->send(new GetUserRequest) // Will return with `['name' => 'Sam']` and status `200`
$forge->send(new GetUserRequest) // Will return with `['name' => 'Alex']` and status `200`
$otherService->send(new GetUserRequest) // Will return with `['error' => 'Server Unavailable']` and status `500`
You may also explicitly define the connector to target with mock responses. Unlike sequence tests, these are always used and are never consumed.
use Saloon\Http\Faking\MockClient;
use Saloon\Http\Faking\MockResponse;
$mockClient = new MockClient([
ForgeConnector::class => MockResponse::make(['name' => 'Sam'], 200),
OtherServiceConnector::class => MockResponse::make(['name' => 'Alex'], 200),
]);
$forge = new ForgeConnector;
$forge->withMockClient($mockClient);
$otherService = new OtherServiceConnector;
$otherService->withMockClient($mockClient);
$forge->send(new GetUserRequest) // Will return with `['name' => 'Sam']` and status `200`
$forge->send(new GetUserRequest) // Will return with `['name' => 'Sam']` and status `200`
$otherService->send(new OtherServiceRequest) // Will return with `['name' => 'Alex']` and status `200`
You may also explicitly define the request to target with mock responses that are used. Unlike sequence tests, these are always used and are never consumed.
use Saloon\Http\Faking\MockClient;
use Saloon\Http\Faking\MockResponse;
$mockClient = new MockClient([
GetForgeServerRequest::class => MockResponse::make(['name' => 'Sam'], 200),
OtherServiceRequest::class => MockResponse::make(['name' => 'Alex'], 200),
]);
$forge = new ForgeConnector;
$forge->withMockClient($mockClient);
$otherService = new OtherServiceConnector;
$otherService->withMockClient($mockClient);
$forge->send(new GetUserRequest) // Will return with `['name' => 'Sam']` and status `200`
$otherService->send(new OtherServiceRequest) // Will return with `['name' => 'Alex']` and status `200`
You can also define fake responses for particular URL patterns. Whenever a request is made for a particular pattern, Saloon will respond to that request.
use Saloon\Http\Faking\MockClient;
use Saloon\Http\Faking\MockResponse;
$mockClient = new MockClient([
'forge.laravel.com/api/*' => MockResponse::make(['name' => 'Sam'], 200),
'samcarre.dev/*' => MockResponse::make(['name' => 'Alex'], 200),
'samcarre.dev/exact' => MockResponse::make(['name' => 'Taylor'], 200), // Exact requests
'*' => MockResponse::make(['name' => 'Wildcard'], 200), // Any other requests
]);
$forge = new ForgeConnector;
$forge->withMockClient($mockClient);
$otherService = new OtherServiceConnector;
$otherService->withMockClient($mockClient);
$forge->send(new GetForgeServerRequest) // Will return with `['name' => 'Sam']` and status `200`
$otherService->send(new OtherServiceRequest) // Will return with `['name' => 'Alex']` and status `200`
$forge->send(new ExactRequest) // Will return with `['name' => 'Taylor']` and status `200`
$forge->send(new WildcardServiceRequest) // Will return with `['name' => 'Wildcard']` and status `200`
When using faking responses, it's important to be able to check that a specific make request was sent and with the correct data, headers, and config. Saloon provides you with various ways to add expectations to your tests.
- AssertSent
- AssertNotSent
- AssertSentJson
- AssertNothingSent
- AssertSentCount
To use one of the expectations, you can simply call the method on your Mock Client.
<?php
// ...
$forge = new ForgeConnector;
$forge->withMockClient($mockClient);
$response = $forge->send(new GetForgeServerRequest(123456));
$mockClient->assertSent(GetForgeServerRequest::class);
The AssertSent / AssertNotSent are the two most powerful expectation methods. They can accept a Saloon request, a URL pattern or even a closure where you define if a request/response is what you expect.
<?php
use Saloon\Http\Request;
use Saloon\Http\Response;
// ...
$forge = new ForgeConnector;
$forge->withMockClient($mockClient);
$response = $forge->send(new GetForgeServerRequest(123456));
$mockClient->assertSent(GetForgeServerRequest::class);
$mockClient->assertSent('/servers/*');
$mockClient->assertSent(function (Request $request, Response $response) {
return $request instanceof GetForgeServerRequest
&& $request->serverId === 123456;
});
Your test may require you to mock your own exceptions that might get thrown. To mock an exception, chain the throw
method after you have defined your mock response.
<?php
$mockClient = new MockClient([
MockResponse::make(['name' => 'Sam'], 200)->throw(new MyException('Something bad!'))
]);
// ...
Sometimes, you may need to return a custom mock response based on the request that is currently trying to be sent. With closure/callable mocking, you can do this. Just provide an anonymous function or an invokable class when defining the mock response, and you will get access to the current PendingRequest
before it is converted into a mock response. This is great if you have stored fixtures based on the request and need to load the fixture data up. This will work with all of the methods above of mocking the request.
<?php
use Saloon\Http\PendingRequest;
$mockClient = new MockClient([
function (PendingRequest $request): MockResponse {
// Write some custom logic here...
return new MockResponse([...]);
},
]);
Once you have written your tests - it's a good idea to ensure that no real API requests are made in the future while running those tests. This is because you could be making requests when you don't intend to which could incur charges or worse, make real changes to data you don't expect. With Saloon, you can prevent stray API requests with the global Config
helper. Simply in your tests, call the Config::preventStrayRequests()
method and you should be good to go!
It's recommended that you place this in your Pest.php
file or in your setUp()
method to make sure it's used on every test.
{% tabs %} {% tab title="Pest.php (PEST)" %}
<?php
use Saloon\Config;
beforeEach(function () {
Config::preventStrayRequests();
});
{% endtab %}
{% tab title="TestCase.php (PHPUnit)" %}
<?php
use Saloon\Config;
class TestCase {
public function setUp()
{
Config::preventStrayRequests();
}
}
{% endtab %} {% endtabs %}