Test and Mock by Using PHP Anonymous Class

Viewed 958

I'm trying to test and mock a piece of code with anonymous class. here is the code:

<?php

namespace App\Service;

use Symfony\Contracts\HttpClient\HttpClientInterface;

class FetchPromoCodeService
{
    private $client;

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

    public function getPromoCodeList(): array
    {
        $response = $this->client->request(
            'GET', 'https://xxxxxxxxx.mockapi.io/test/list'
        );

        return $response->toArray();
    }
}

Here is my test Class:

<?php

namespace App\Tests\Service;

use App\Service\FetchPromoCodeService;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\JsonResponse;

class FetchPromoCodeServiceTest extends TestCase
{

    public function testGetPromoCodeList()
    {
        $fetchPromoCodeService = new class () extends FetchPromoCodeService {
            public $client;

            public function __construct($client)
            {
                $this->client = (new JsonResponse(['a' => 1, 'b' => 2]))->getContent();
                parent::__construct($client);
            }
        };

        $result = $fetchPromoCodeService->getPromoCodeList();

        $this->assertIsArray($result);
    }
}

I need to test the getPromoCodeList() method so I want to mock the http call. The purpose of the test is to make sure that we convert the result call into a php array.

But my issue is with the FetchPromoCodeService constructor. I have this error when I execute the command.

ArgumentCountError: Too few arguments to function class@anonymous::__construct(), 0 passed in /var/www/html/tests/Service/FetchPromoCodeServiceTest.php on line 14 and exactly 1 expected

I understand that it is waiting for an argument of type HttpClientInterface but I know that there is a way to override that as I just want to mock client property. I just don't remember how I can do it.

How can I do that in php and anonymous classes?

2 Answers

Although you could achieve want you want using an anonymous class the mock client that comes with the Symfony HTTP client is probably the easier solution:

<?php

namespace App\Tests\Service;

use App\Service\FetchPromoCodeService;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\Response\MockResponse;

final class FetchPromoCodeServiceTest extends TestCase
{
    public function testGetPromoCodeList()
    {
        $client = new MockHttpClient([new MockResponse(json_encode(['a' => 1, 'b' => 2]))]);

        $result = (new FetchPromoCodeService($client))->getPromoCodeList();

        self::assertIsArray($result);
    }
}

@subCore Tell me if I'm wrong but according to what you said, I have done it this way and it seems to work.

<?php

namespace App\Tests\Service;

use App\Service\FetchPromoCodeService;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Contracts\HttpClient\HttpClientInterface;

class FetchPromoCodeServiceTest extends TestCase
{
    public function testGetPromoCodeList()
    {
        $client = $this->createMock(HttpClientInterface::class);
        $fetchPromoCodeService = new class ($client) extends FetchPromoCodeService {
            public $client;
            public function __construct($client)
            {
                $codesMock = ['a' => 1, 'b' => 2];
                $this->client = (new JsonResponse($codesMock))->getContent(); // mock client object;
                parent::__construct($client);
            }
        };

        $result = $fetchPromoCodeService->getPromoCodeList();

        $this->assertIsArray($result);
    }
}
Related