Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature | Global Mock Client #359

Merged
merged 11 commits into from
Jan 18, 2024
1 change: 0 additions & 1 deletion src/Helpers/Helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ public static function value(mixed $value, mixed ...$args): mixed
* Check if a class is a subclass of another.
*
* @param class-string $class
* @throws \ReflectionException
*/
public static function isSubclassOf(string $class, string $subclass): bool
{
Expand Down
50 changes: 37 additions & 13 deletions src/Http/Faking/MockClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,17 @@ class MockClient
*/
protected array $recordedResponses = [];

/**
* Global Mock Client
*
* Use MockClient::global() to register a global mock client
*/
protected static ?MockClient $globalMockClient = null;

/**
* Constructor
*
* @param array<\Saloon\Http\Faking\MockResponse|\Saloon\Http\Faking\Fixture|callable> $mockData
* @throws \Saloon\Exceptions\InvalidMockResponseCaptureMethodException
*/
public function __construct(array $mockData = [])
{
Expand All @@ -67,7 +73,6 @@ public function __construct(array $mockData = [])
* Store the mock responses in the correct places.
*
* @param array<\Saloon\Http\Faking\MockResponse|\Saloon\Http\Faking\Fixture|callable> $responses
* @throws \Saloon\Exceptions\InvalidMockResponseCaptureMethodException
*/
public function addResponses(array $responses): void
{
Expand All @@ -82,8 +87,6 @@ public function addResponses(array $responses): void

/**
* Add a mock response to the client
*
* @throws \Saloon\Exceptions\InvalidMockResponseCaptureMethodException
*/
public function addResponse(MockResponse|Fixture|callable $response, ?string $captureMethod = null): void
{
Expand Down Expand Up @@ -238,8 +241,6 @@ public function getLastResponse(): ?Response

/**
* Assert that a given request was sent.
*
* @throws \ReflectionException
*/
public function assertSent(string|callable $value): void
{
Expand All @@ -250,8 +251,6 @@ public function assertSent(string|callable $value): void

/**
* Assert that a given request was not sent.
*
* @throws \ReflectionException
*/
public function assertNotSent(string|callable $request): void
{
Expand All @@ -264,7 +263,6 @@ public function assertNotSent(string|callable $request): void
* Assert JSON data was sent
*
* @param array<string, mixed> $data
* @throws \ReflectionException
*/
public function assertSentJson(string $request, array $data): void
{
Expand All @@ -291,8 +289,6 @@ public function assertSentCount(int $count): void

/**
* Check if a given request was sent
*
* @throws \ReflectionException
*/
protected function checkRequestWasSent(string|callable $request): bool
{
Expand All @@ -315,8 +311,6 @@ protected function checkRequestWasSent(string|callable $request): bool

/**
* Check if a request has not been sent.
*
* @throws \ReflectionException
*/
protected function checkRequestWasNotSent(string|callable $request): bool
{
Expand Down Expand Up @@ -373,6 +367,36 @@ public function findResponseByRequestUrl(string $url): ?Response
return null;
}

/**
* Register a global mock client
*
* This will register a global mock client that is available throughout the
* application's lifecycle. You should destroy the global mock client
* after each test using MockClient::destroyGlobal().
*
* @param array<\Saloon\Http\Faking\MockResponse|\Saloon\Http\Faking\Fixture|callable> $mockData
*/
public static function global(array $mockData = []): MockClient
{
return static::$globalMockClient ??= new static($mockData);
}

/**
* Get the global mock client if it has been registered
*/
public static function getGlobal(): ?MockClient
{
return static::$globalMockClient;
}

/**
* Destroy the global mock client
*/
public static function destroyGlobal(): void
{
static::$globalMockClient = null;
}

/**
* Test if the closure can pass with the history.
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Http/PendingRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public function __construct(Connector $connector, Request $request, MockClient $
$this->method = $request->getMethod();
$this->url = URLHelper::join($this->connector->resolveBaseUrl(), $this->request->resolveEndpoint());
$this->authenticator = $request->getAuthenticator() ?? $connector->getAuthenticator();
$this->mockClient = $mockClient ?? $request->getMockClient() ?? $connector->getMockClient();
$this->mockClient = $mockClient ?? $request->getMockClient() ?? $connector->getMockClient() ?? MockClient::getGlobal();

// Now, we'll register our global middleware and our mock response middleware.
// Registering these middleware first means that the mock client can set
Expand Down
4 changes: 0 additions & 4 deletions tests/Pest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@

use Saloon\Tests\Fixtures\Connectors\TestConnector;

expect()->extend('toBeOne', function () {
return $this->toBe(1);
});

/*
|--------------------------------------------------------------------------
| Functions
Expand Down
60 changes: 60 additions & 0 deletions tests/Unit/GlobalMockClientTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

declare(strict_types=1);

use Saloon\Http\Faking\MockClient;
use Saloon\Http\Faking\MockResponse;
use Saloon\Tests\Fixtures\Requests\UserRequest;
use Saloon\Tests\Fixtures\Connectors\TestConnector;

afterEach(function () {
MockClient::destroyGlobal();
});

test('can create a global mock client', function () {
$mockClient = MockClient::global([
MockResponse::make(['name' => 'Sam']),
]);

expect($mockClient)->toBeInstanceOf(MockClient::class);
expect(MockClient::getGlobal())->toBe($mockClient);

$connector = new TestConnector;
$response = $connector->send(new UserRequest);

expect($response->isMocked())->toBeTrue();
expect($response->json())->toEqual(['name' => 'Sam']);

$mockClient->assertSent(UserRequest::class);
});

test('the mock client can be destroyed', function () {
$mockClient = MockClient::global();

expect(MockClient::getGlobal())->toBe($mockClient);

MockClient::destroyGlobal();

expect(MockClient::getGlobal())->toBeNull();
});

test('a local mock client is given priority over the global mock client', function () {
MockClient::global([
MockResponse::make(['name' => 'Sam']),
]);

$localMockClient = new MockClient([
MockResponse::make(['name' => 'Taylor']),
]);

$connector = new TestConnector;
$connector->withMockClient($localMockClient);

$response = $connector->send(new UserRequest);

expect($response->isMocked())->toBeTrue();
expect($response->json())->toEqual(['name' => 'Taylor']);

$localMockClient->assertSentCount(1);
MockClient::global()->assertNothingSent();
});