saionachi
saionachi

Reputation: 427

How to mock API calls inside controllers in phpunit/laravel

I'm creating unit tests in Laravel 8 using phpunit and I have a controller that uses external API to get data for my functions using Guzzle HTTP Client.

I placed the method to call the guzzle inside the Controller.php so it can be accessed in any other controller by extending this class

class Controller extends BaseController
{
    protected function getAPI($url, $params = [])
    {
        $test_api = new Client();
        $headers = [
             'form_params' => $params,
             'http_errors' => false
        ];
            
        $response = $test_api->get($url, $headers);

        // More stuff to get the response...
    }
}

On other controllers I called the method to do the API call

class OtherController extends Controller
{
     public function someMethod()
     {
          $response = $this->getAPI('/get_data', [], 'get');

          // More stuffs...
     }
}

The unit test I'm creating is for someMethod() and I don't know how to mock the API call inside this controller that has been extended on other controller.

What I wanted is to mock the API call so I don't need to actually "call" the API. I checked the documentation of guzzle about testing (https://docs.guzzlephp.org/en/stable/testing.html#mock-handler) but it doesn't make sense on how to implement this on my scenario.

Upvotes: 2

Views: 2754

Answers (1)

RobbyKrlos
RobbyKrlos

Reputation: 121

You can use PHPUnit:

use Tests\TestCase;

[...]

public function testApiOtherControllerSomeMethod(){

    //Prepare your sample data that mocks API result
    //or create a variable with your wanted api mocked data.
    $apiTestData = Storage::disk('test-data-api')
        ->get('apiTestData.txt');

    //Using the PHPUnit MockBuilder, target the class and method
    //you want to mock. In your case, you mock the inherited method
    //"getAPI" from Controller, in OtherController
    $apiMock = $this->getMockBuilder(OtherController::class)
        ->onlyMethods(['getAPI'])
        ->getMock();
    
    //use the mock object to change the method's return output
    $apiMock->expects($this->any())
        ->method('getAPI')
        ->willReturn($apiTestData);

    //now the mock object will behave like the OtherController class
    //and you can now call "someMethod", which will return the mocked
    //data when calling getAPI() internally.
    $testResult = $apiMock->someMethod();

    //$this->assert... your returned mocked data.
}

As a side note, you should probably move the API handling from the main Controller class, into an API service.

Upvotes: 2

Related Questions