Skip to content

[9.x] Allow forcing requests made via the Http client to be faked #42230

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

Merged
merged 4 commits into from
May 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion src/Illuminate/Http/Client/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@ class Factory
*/
protected $responseSequences = [];

/**
* Indicates that an exception should be thrown if any request is not faked.
*
* @var bool
*/
protected $preventStrayRequests = false;

/**
* Create a new factory instance.
*
Expand Down Expand Up @@ -220,6 +227,29 @@ public function stubUrl($url, $callback)
});
}

/**
* Indicate that an exception should not be thrown if any request is not faked.
*
* @param bool $prevent
* @return $this
*/
public function preventStrayRequests($prevent = true)
{
$this->preventStrayRequests = $prevent;

return $this;
}

/**
* Indicate that an exception should not be thrown if any request is not faked.
*
* @return $this
*/
public function allowStrayRequests()
{
return $this->preventStrayRequests(false);
}

/**
* Begin recording request / response pairs.
*
Expand Down Expand Up @@ -390,7 +420,7 @@ public function __call($method, $parameters)
}

return tap($this->newPendingRequest(), function ($request) {
$request->stub($this->stubCallbacks);
$request->stub($this->stubCallbacks)->preventStrayRequests($this->preventStrayRequests);
})->{$method}(...$parameters);
}
}
25 changes: 25 additions & 0 deletions src/Illuminate/Http/Client/PendingRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Illuminate\Support\Traits\Macroable;
use JsonSerializable;
use Psr\Http\Message\MessageInterface;
use RuntimeException;
use Symfony\Component\VarDumper\VarDumper;

class PendingRequest
Expand Down Expand Up @@ -138,6 +139,13 @@ class PendingRequest
*/
protected $stubCallbacks;

/**
* Indicates that an exception should be thrown if any request is not faked.
*
* @var bool
*/
protected $preventStrayRequests = false;

/**
* The middleware callables added by users that will handle requests.
*
Expand Down Expand Up @@ -1040,6 +1048,10 @@ public function buildStubHandler()
->first();

if (is_null($response)) {
if ($this->preventStrayRequests) {
throw new RuntimeException('Attempted request to ['.(string) $request->getUri().'] without a matching fake.');
}

return $handler($request, $options);
}

Expand Down Expand Up @@ -1123,6 +1135,19 @@ public function stub($callback)
return $this;
}

/**
* Indicate that an exception should be thrown if any request is not faked.
*
* @param bool $prevent
* @return $this
*/
public function preventStrayRequests($prevent = true)
{
$this->preventStrayRequests = $prevent;

return $this;
}

/**
* Toggle asynchronicity in requests.
*
Expand Down
13 changes: 13 additions & 0 deletions src/Illuminate/Support/Facades/Http.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
* @method static \Illuminate\Http\Client\Response post(string $url, array $data = [])
* @method static \Illuminate\Http\Client\Response put(string $url, array $data = [])
* @method static \Illuminate\Http\Client\Response send(string $method, string $url, array $options = [])
* @method static \Illuminate\Http\Client\Factory allowStrayRequests()
* @method static void assertSent(callable $callback)
* @method static void assertSentInOrder(array $callbacks)
* @method static void assertNotSent(callable $callback)
Expand Down Expand Up @@ -93,6 +94,18 @@ public static function fakeSequence(string $urlPattern = '*')
return $fake->fakeSequence($urlPattern);
}

/**
* Indicate that an exception should be thrown if any request is not faked.
*
* @return \Illuminate\Http\Client\Factory
*/
public static function preventStrayRequests()
{
return tap(static::getFacadeRoot(), function ($fake) {
static::swap($fake->preventStrayRequests());
});
}

/**
* Stub the given URL using the given callback.
*
Expand Down
18 changes: 18 additions & 0 deletions tests/Http/HttpClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use OutOfBoundsException;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\TestCase;
use RuntimeException;
use Symfony\Component\VarDumper\VarDumper;

class HttpClientTest extends TestCase
Expand Down Expand Up @@ -1527,4 +1528,21 @@ public function testRequestExceptionIsNotThrownIfConditionIsNotSatisfied()

$this->assertSame('{"result":{"foo":"bar"}}', $response->body());
}

public function testItCanEnforceFaking()
{
$this->factory->preventStrayRequests();
$this->factory->fake(['https://vapor.laravel.com' => Factory::response('ok', 200)]);
$this->factory->fake(['https://forge.laravel.com' => Factory::response('ok', 200)]);

$responses = [];
$responses[] = $this->factory->get('https://vapor.laravel.com')->body();
$responses[] = $this->factory->get('https://forge.laravel.com')->body();
$this->assertSame(['ok', 'ok'], $responses);

$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('Attempted request to [https://laravel.com] without a matching fake.');

$this->factory->get('https://laravel.com');
}
}
7 changes: 7 additions & 0 deletions tests/Support/SupportFacadesHttpTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,11 @@ public function testFacadeRootIsSharedWhenStubbingUrls(): void
$factory = $this->app->make(Factory::class);
$this->assertSame('OK!', $factory->get('https://laravel.com')->body());
}

public function testFacadeRootIsSharedWhenEnforcingFaking(): void
{
$client = Http::preventStrayRequests();

$this->assertSame($client, $this->app->make(Factory::class));
}
}