anabeto93
anabeto93

Reputation: 337

Overwrite Laravel Http Faker

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

Answers (4)

Tschallacka
Tschallacka

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

Mehul Kaushik
Mehul Kaushik

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

Healyhatman
Healyhatman

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

ferhsom
ferhsom

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

Related Questions