I am looking for some guidance on how to write a unit test for Symfony Service class. All day hunting down the web but what I mostly find is outdated questions and answers regarding old phpunit versions and old Symfony versions.
I am running Symfony 4 and have a service class called ApiService.php. This class connects to an external API service, I am not looking at testing this external API service but rather my own methods with a fixed dataset.
A very towned down version of the class is like this and lives in the folder src/Service/ApiService.php:
<?php
namespace App\Service;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Psr7\Uri;
use JsonException;
class ApiService
{
/**
* Set if test environment is enabled
*
* @var bool
* @since 1.0.0
*/
private bool $test;
/**
* User key for API authentication
*
* @var string
* @since 1.0.0
*/
private string $userKey;
/**
* Construct the class.
*
* @param bool $test Set API mode
* @param string $key Set the API token
*
* @since 1.0.0
*/
public function __construct(bool $test, string $key)
{
$this->userKey = $key;
$this->test = $test;
}
/**
* Search companies.
*
* @param array $params Parameters to filter the query on
* @param array $companies List of retrieved companies
*
* @return array List of companies.
*
* @since 1.0.0
* @throws JsonException
* @throws GuzzleException
*/
public function getCompanies(array $params, array $companies = []): array
{
$results = $this->callApi('search/companies', $params);
if (isset($results['data']['items'])) {
$companies = array_merge(
$companies,
$results['data']['items']
);
}
$nextLink = $results['data']['nextLink'] ?? null;
if ($nextLink) {
$uri = new Uri($nextLink);
parse_str($uri->getQuery(), $params);
$companies = $this->getCompanies($params, $companies);
}
return $companies;
}
/**
* Call the API.
*
* @param string $destination The endpoint to call
* @param array $params The parameters to pass to the API
*
* @return array API details.
*
* @since 1.0.0
* @throws GuzzleException|JsonException
*/
private function callApi(string $destination, array $params = []): array
{
$client = new Client(['base_uri' => 'https://test.com/']);
if ($this->test) {
$destination = 'test' . $destination;
}
if ($this->userKey) {
$params['user_key'] = $this->userKey;
}
$response = $client->get($destination, ['query' => $params]);
return json_decode(
$response->getBody()->getContents(),
true,
512,
JSON_THROW_ON_ERROR
);
}
}
Here is the test class I have ended up with so far but it does not work:
<?php
namespace App\Tests\Service;
use App\Service\ApiService;
use PHPUnit\Framework\TestCase;
class ApiServiceTest extends TestCase
{
public function testGetCompanies()
{
$result = ['data' => [
'items' => [
1 => 'first',
2 => 'second'
]
];
$apiService = $this->getMockBuilder(ApiService::class)
->disableOriginalConstructor()
->getMock();
$apiService->method('callApi')
->with($result);
$result = $apiService->getCompanies([]);
print_r($result);
}
}
What I failing to understand is a couple of things.
First which class I should extend:
- TestCase
- WebTestCase
- KernelTestCase
Second, how do I setup mock data so I am not going the external API but rather pass in the $result I have defined.
As mentioned earlier, I am not looking to test the external API but rather that my methods always behave as designed in the test given the sample data to test with.
Any tips will be greatly appreciated.