Reputation: 337
Is there a way to overwrite values of Http::fake([])
in Laravel during testing. I've noticed that if I set a value during a faker, eg. Http::fake(['url1.com' => Http::response('OK'), 'url2.com' => Http::response('Not Found', 404),])
, if for some reason I need to change the value of say url1.com
to something else such as ['message' => 'Success']
, if I "update" the value by calling Http::fake(['url1.com' => Http::response(['message' => 'Success'])
again at a later point, I'd expect the response when I call Http::get('url1.com')
to return ['message' => 'Success']
but it instead always returns OK
which was the original value set.
Same way if I later call Http::fake(['url2.com' => Http::response(['message' => 'Object found.'])])
, I would expect the response when I call Http::get('url2.com')
to be ['message' => 'Object found.']
but it'll always return Not found
which was the original value set.
Upvotes: 3
Views: 1565
Reputation: 28722
I have the use case that I only want one url to be fixed, instead of all url's being fixed as I have a fixture that loads all auxiliary data needed for integration tests, and I don't want to have to reload all those with a cleared stubs.
I developed this helper function to clear the existing http fakes that match on the given url. Note, this is programmed on a laravel 9 app. Will update when we upgrade.
use Illuminate\Support\Collection;
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Http;
use GuzzleHttp\Psr7\Request as GuzzleRequest;
use Illuminate\Http\Client\Factory as HttpClientFactory;
if (!function_exists('clearExistingFakes')) {
function clearExistingFakes($url): void
{
$priceCache = app(PricesCache::class);
$priceCache->clear();
/** @var HttpClientFactory $target */
$target = Http::getFacadeRoot();
$reflection = new \ReflectionObject($target);
$property = $reflection->getProperty('stubCallbacks');
$property->setAccessible(true);
/** @var Collection<int, Closure> $callbacks */
$callbacks = $property->getValue(Http::getFacadeRoot());
$guzzleRequest = new GuzzleRequest('GET', $url);
$request = new Request($guzzleRequest);
$tounset = [];
foreach ($callbacks as $key => $callback) {
$matchingResponse = $callback($request, ['on_stats' => function ($transferStats) { }]);
if (!is_null($matchingResponse)) {
$tounset[] = $key;
}
}
$callbacks->forget($tounset);
$property->setValue($target, $callbacks);
}
}
Upvotes: 0
Reputation: 1
I did the following:
// Added this before doing the Http::fake()
$realHttp = app(\Illuminate\Http\Client\Factory::class);
// Added this when I needed to reset it.
Http::swap($realHttp);
Worked fine after doing this. This is on Laravel 10.x
Upvotes: 0
Reputation: 1659
@ferhsom's answer worked for me in Laravel 8.x but not in Laravel 9.x, where I had to use the answer from https://laracasts.com/discuss/channels/testing/how-to-delete-instance-of-httpfake-in-app
I added the following method to the base TestCase class
protected function clearExistingFakes(): static
{
$reflection = new \ReflectionObject(Http::getFacadeRoot());
$property = $reflection->getProperty('stubCallbacks');
$property->setAccessible(true);
$property->setValue(Http::getFacadeRoot(), collect());
return $this;
}
Upvotes: 4
Reputation: 236
You can use achieve this by swapping the Http client
Http::fake(['https://potato.com' => Http::response('Yummy!', 200)]);
dump(Http::get('https://potato.com')->body()); // Yummy!
at some point in your test, you can reset the Http Facade using Facade Swap
Http::swap(app(\Illuminate\Http\Client\Factory::class));
now you can change the fake value to whatever you want
Http::fake(['https://potato.com' => Http::response("That's what the dcotor ordered.", 200)]);
dump(Http::get('https://potato.com')->body()); // That's what the doctor orderd.
Upvotes: 4