Testing / Mocking Laravel HTTP Client Response / Request
Published: 2023-12-29 11:45:29

---
title: Testing / Mocking Laravel HTTP Client Response / Request
published: true
description: 
tags: 
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-12-29 11:40 +0000
---

In my recent coding endeavors, I unexpectedly found myself immersed in the world of writing tests—a departure from my usual programming routine. The particular challenge that caught me off guard was the intricacy of mocking objects, especially when dealing with Laravel Http Client requests and responses.
Let me walk you through a recent scenario. I've been working on a BlockchainService class with a critical function: getConfirmations(string $hash): int. This function interacts with other microservices by making HTTP requests and handling responses.
Here's a snippet of the function's definition:
```php
public function getConfirmations(string $hash, array $context = []): int
    {
        try {
            $token = $this->authServiceV2->getClientCredentialsToken();
            $response = $this->request
                ->throw()
                ->get('/api/internal/v2/blockchain/confirmations/' .  $hash);

            $json = $response->json();
            $json = [];

            return $json['confirmations'];
        } catch (RequestException $exception) {
            throw new InternalCallException($exception);
        }
    }

```
Now, the challenges I faced were multi-faceted:

###1. Mocking Illuminate\Http\Client\PendingRequest:

I crafted a utility function in my TestCase class to create a mock object for PendingRequest. This involved setting up the expected behavior for the get method and creating a mock response using Guzzle.

```php
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Http\Client\Response as LaravelResponse;
use PHPUnit\Framework\MockObject\MockObject;

abstract class TestCase extends BaseTestCase
{
  protected \Exception $exceptionToThrow;
 
  public function getHttpResponseMock($status, $headers = [], $body = [])
  {
      $mock = \Mockery::mock(PendingRequest::class)->makePartial();
  
      $laravelResponse = $this->createLaravelHttpResponse($status, $headers, $body);
  
      $mock->shouldReceive('withMiddleware')
          ->andReturn($mock);
  
      $mock->shouldReceive('get')
          ->andReturnUsing(function () use ($laravelResponse) {
              if (isset($this->exceptionToThrow)) {
                  throw $this->exceptionToThrow;
              }
              return $laravelResponse;
          })
          ->once();
  
      return $mock;
  }


  public function createLaravelHttpResponse($status, $headers = [], $body = []): LaravelResponse
  {
      $guzzleResponse = new \GuzzleHttp\Psr7\Response($status, $headers, json_encode($body));

      return new LaravelResponse($guzzleResponse);
  }
}
```

### 2. Mocking AuthServiceV2: 

To mock the AuthServiceV2, I created a partial mock with expectations for the getClientCredentialsToken method.

```php
use App\Services\AuthServiceV2;
use PHPUnit\Framework\MockObject\MockObject;

public function createAuthServiceV2PartialMock(): MockObject
{
    $authService = $this->createMock(AuthServiceV2::class);
    $authService
        ->expects($this->once())
        ->method('getClientCredentialsToken')
        ->willReturn('mocked_token');

    return $authService;
}
```

### 3. Making Assertions on the Function:

Bringing it all together in a test method, I validated that the getConfirmations function behaves as expected by asserting the result against the expected confirmations.

```php
public function it_can_get_confirmations()
{
    $expectedConfirmations = 10;
    $hash = 'example_hash';

    $pendingRequest = $this->getHttpResponseMock(200, [], [ 'confirmations' => $expectedConfirmations ]);
    $authService = $this->createAuthServiceV2PartialMock();

    // Create instance
    $blockchainService = new BlockchainService($pendingRequest, $authService);

    // Act
    $confirmations = $blockchainService->getConfirmations($hash);

    // Assert
    $this->assertEquals($expectedConfirmations, $confirmations);

```

This cohesive approach ensured a comprehensive testing environment, covering the intricacies of HTTP client mocking and service authentication. The utilization of mock objects and thoughtful assertions allowed for robust validation of the getConfirmations function, providing confidence in its functionality.

In the ever-evolving landscape of software development, navigating testing complexities becomes an integral part of ensuring the reliability and robustness of our code. Embracing challenges and employing effective testing strategies are key steps toward code excellence and maintainability. Here's to conquering the testing terrain! 🚀✨

Originally posted at: https://jcfrane.com