Вы должны перейти от PHPUnit TestCase
. WebTestCase
и KernelTestCase
полезны, если вы хотите провести функциональные тесты. Ваш случай представляет собой модульный тест classi c: вы хотите протестировать свой ApiService
изолированно.
ApiService
на самом деле сейчас выполняет две вещи:
- Выполнение вызовов
- Обработка данных
Хорошая идея - отделить один от другого, представив специальный клиент API:
interface ApiClient
{
public function call(string $destination, array $params = []): array;
}
Для вашего производственного кода , вы можете создать реализацию с помощью Guzzle. Вы можете написать интеграционные тесты для GuzzleApiClient
, которые выполняют фактические HTTP-запросы, чтобы гарантировать, что он обрабатывает ответы ожидаемым образом.
Ваш ApiService
теперь сводится к следующему:
final class ApiService
{
private ApiClient $apiClient;
public function __construct(ApiClient $apiClient)
{
$this->apiClient = $apiClient;
}
public function getCompanies(array $params, array $companies = []): array
{
$results = $this->apiClient->call('search/companies', $params);
if (isset($results['data']['items'])) {
$companies = array_merge(
$companies,
$results['data']['items']
);
}
$nextLink = $results['data']['nextLink'] ?? null;
if ($nextLink) {
parse_str(parse_url($nextLink, PHP_URL_QUERY), $params);
$companies = $this->getCompanies($params, $companies);
}
return $companies;
}
}
Поскольку я не знаю, что именно делает ApiService
, я составил следующие примеры тестов:
/**
* @covers \App\Service\ApiService
*/
class ApiServiceTest extends TestCase
{
/**
* @var MockObject&ApiClient
*/
private ApiClient $apiClient;
private ApiService $subject;
public function testGetCompanies()
{
$this->apiClient->addResponse(
'search/companies',
[],
['data' => ['items' => [1 => 'first', 2 => 'second']]]
);
$result = $this->subject->getCompanies([]);
self::assertEquals(['first', 'second'], $result);
}
public function testGetCompaniesPaginated()
{
$this->apiClient->addResponse(
'search/companies',
[],
['data' => ['items' => [1 => 'first', 2 => 'second'], 'nextLink' => 'search/companies?page=2']]
);
$this->apiClient->addResponse(
'search/companies',
['page' => 2],
['data' => ['items' => [1 => 'third', 2 => 'fourth'], 'nextLink' => 'search/companies?page=3']]
);
$this->apiClient->addResponse(
'search/companies',
['page' => 3],
['data' => ['items' => [1 => 'fifth']]]
);
$result = $this->subject->getCompanies([]);
self::assertEquals(['first', 'second', 'third', 'fourth', 'fifth'], $result);
}
protected function setUp(): void
{
parent::setUp();
$this->apiClient = new class implements ApiClient {
private array $responses = [];
public function call(string $destination, array $params = []): array
{
return $this->responses[$this->key($destination, $params)] ?? [];
}
public function addResponse(string $destination, array $params, array $response)
{
$this->responses[$this->key($destination, $params)] = $response;
}
private function key(string $destination, array $params): string
{
return $destination . implode('-', $params);
}
};
$this->subject = new ApiService($this->apiClient);
}
}
Я создал анонимный класс для реализации ApiClient
. Это всего лишь пример. Вы, конечно, можете также использовать mocks PHPUnit, Prophecy или любой другой фреймворк mocking, который вам нравится. Но я обнаружил, что часто проще создавать специализированные тестовые двойники.