Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

eugenefvdm's avatar

How do you simulate an HTTP Get request to a 3rd party API?

I just don't get how to do an HTTP Get simulation. I have to call a 3rd party billing API and get subscription status. One I receive the status I have to look up the subscription_id in a database and update the status and amount of money in the database.

I've reviewed all four these links for documentation but I still don't get it: https://laravel.com/docs/8.x/testing https://laravel.com/docs/8.x/http-client#testing [no GET examples] https://laravel.com/docs/8.x/http-tests https://laravel.com/docs/8.x/mocking [refers to http testing]

I don't understand if I'm supposed to "mock", or not. So far my code looks like this. The array reflects the exact values I'm getting back from the 3rd party API.

public function test_fetching_a_subscription()
    {      
        // create a fake HTTP Get request  
        $result = Http::fake([
            'https://api.example.com/subscriptions/d5a98126/fetch' => Http::response([
                'code' => 200,
                'status' => "success",
                'data' => [
                    'response' => [
                        'amount' => 502,
                        'cycles' => 0,
                        'cycles_complete' => 0,
                        'frequency' => 3,
                        'run_date' => '2022-01-14T00:00:00+02:00',
                        'status' => 1,
                        'status_reason' => '',
                        'status_text' => 'ACTIVE',
                        'subscription_id' => 'd5a98126',
                    ]
                ],
            ], 200),
        ]);

        // Store result in fake database

        // Check if subscription_id's amount has been updated
    }

Questions:

  1. Am I supposed to Mock?
  2. Am I supposed to use $this assertions?
  3. How?

For the code above for now I just want to get "real" / fake values from the "simulated GET".

Honestly I work with APIs every day and spend hours testing using live data but doing it using PHPUnit and Laravel just seems like such a big mission. Please help me out of my misery.

0 likes
2 replies
tykus's avatar

Http::fake is the mock.

You are setting up your test to fake an outgoing request to https://api.example.com/subscriptions/d5a98126/fetch - this supposes that you are going to interact with application code (a Controller action for example) that makes that outbound Request. Is this the case, and what does the rest of your test look like?

For example:

       $result = Http::fake([
            'https://api.example.com/subscriptions/d5a98126/fetch' => Http::response([
                'code' => 200,
                'status' => "success",
                'data' => [
                    'response' => [
                        'amount' => 502,
                        'cycles' => 0,
                        'cycles_complete' => 0,
                        'frequency' => 3,
                        'run_date' => '2022-01-14T00:00:00+02:00',
                        'status' => 1,
                        'status_reason' => '',
                        'status_text' => 'ACTIVE',
                        'subscription_id' => 'd5a98126',
                    ]
                ],
            ], 200),
        ]);

        $response = $this->get('/');

		// this is a spurious assertion, but an example of using the Http facade for assertions
        Http::assertSent(function ($request) {
            return $request->url() == 'https://api.example.com/subscriptions/d5a98126/fetch'
        });

What every you want to fetch from the database should be put there in the arrange phase of your test example.

eugenefvdm's avatar

@tykus thanks for your reply.

should be put there in the arrange phase

When I read that sentence about "arrange" I realised I haven't provided enough information.

In the meanwhile I had to rewrite my API wrapper because I instantiated the 3rd party API called PayFast in the constructor of my service provider method instead of just passing the already instantiated client.

So to be more clear about what I'm trying to do:

  1. Make an API call to a 3rd party payment service to fetch subscriptions information.

This API call is a GET request and takes this format: https://api.payfast.co.za/subscriptions/dc0521d3-55fe-269b-fa00-b647310d760f/fetch

  1. If the GET request is successful, the API then typically returns something like I've showed in the array:
'code' => 200,
                'status' => "success",
                'data' => [
                    'response' => [
                        'amount' => 502,
                        'cycles' => 0,
                        'cycles_complete' => 0,
                        'frequency' => 3,
                        'run_date' => '2022-01-14T00:00:00+02:00',
                        'status' => 1,
                        'status_reason' => '',
                        'status_text' => 'ACTIVE',
                        'subscription_id' => 'dc0521d3-55fe-269b-fa00-b647310d760f',
                    ]
                ]
  1. I'm trying to Mock / Simulate / Test / Reproduce this response without calling the API.

I believe I could be facing a problem that http "mocking" won't just work if the 3rd party API doesn't use the Laravel Http facade. From looking at the 3rd party API it appears they are using a Guzzle client.

Anyway so far this is my code, and in the comments I show where instead of "mocking" I am getting real API results causing tests to take forever.

(The API has test mode and live, and I've check in test mode the URL stays the same so I don't think it's a case of not 'intercepting' the correct URL.)

Service Provider:

$this->app->bind('payfast-api', function ($app) {
            ray('Binding 3rd party API to my API');
            
            $client = new PayFastApi([
                    'merchantId' => config('payfast.merchant_id'),            
                    'passPhrase' => config('payfast.passphrase'),
                    'testMode' => config('payfast.testmode'),   
            ]);

            return new FintechSystemsPayFastApi($client);
        });

Minimalist wrapper around API call:

<?php

namespace FintechSystems\Payfast;

use PayFast\PayFastApi as PayFastApiClient;
use FintechSystems\Payfast\Contracts\PaymentGateway;

class PayFastApi implements PaymentGateway
{    
    private $client;

    public function __construct(PayFastApiClient $client)
    {
        $this->client = $client;       
    }

    public function fetchSubscription($token)
    {
        $fetchArray = $this->client->subscriptions->fetch($token);

        return $fetchArray;
    }

}

Tests which are calling the real API instead of a "mock".

public function test_fetching_a_subscription()
    {        
        $response = Http::response(['status' => 'success']);

        Http::fake(['api.payfast.co.za/*' => $response]);

		// The line below still calls the API, instead of a "mock"
        $this->assertEquals(['status' => 'success'], PayFastApi::fetchSubscription('dc0521d3-55fe-269b-fa00-b647310d760f'));
    }

Please or to participate in this conversation.