Я занялся идеей, написать функциональные тесты (хотя, сейчас оглядываясь она выглядит глупой) у меня возникли некоторые «сложности», так как почти каждая сущность связана со сторонним запросом. Как правильно замокать сервисы, чтобы это не стало муторным делом?
Например, есть такая сущность, в которой происходит определённая проверка
#[ORM\Table(name: "cp_enterprise_dispatchers")]
#[ORM\Entity]
class EnterpriseDispatcher implements AggregateRoot
{
#[ORM\Id]
#[ORM\Column(name: "id", type: "integer", nullable: false)]
#[ORM\GeneratedValue(strategy: "IDENTITY")]
private int $id;
//...остальные поля...
public function __construct(
EnterpriseDispatcherServiceInterface $service, string $name, string $url, string $login, string $password, bool $enabled = true
)
{
$this->name = $name;
$this->url = $url;
$this->login = $login;
$this->password = $password;
$this->solidcpLoginId = $service->getEnterpriseDispatcherRealUserId($url, $login, $password);
$this->enabled = $enabled;
$this->solidcpServers = new ArrayCollection();
$this->recordEvent(new Event\EnterpriseDispatcherCreated($this));
}
//гетеры,
}
Сам сервис, который общается с другим сервером.
final readonly class EnterpriseDispatcherService implements EnterpriseDispatcherServiceInterface
{
public function __construct(
private EsUsers $esUsers
) {}
public function getEnterpriseDispatcherRealUserId(string $url, string $login, string $password): int
{
$this->esUsers->initManual($url, $login, $password);
try {
$result = $this->esUsers->getUserByUsername($login);
} catch (\Exception $e) {
throw new \DomainException("Soap execution error (Code: {$e->getCode()}, Message: {$e->getMessage()})", $e->getCode(), $e);
}
if ($result['IsPeer']) {
throw new \DomainException("This Login {$login} is Peer. Please use a real User, not Peer");
}
return (int)$result['UserId'];
}
}
Чтобы его замокать, и при этом Симфони не ругался:
Symfony\Component\DependencyInjection\Exception\InvalidArgumentException:
The "App\Service\Service" service is already initialized, you cannot replace it.
Мне приходиться делать мок класс:
final class EnterpriseDispatcherServiceMock implements EnterpriseDispatcherServiceInterface
{
public function getEnterpriseDispatcherRealUserId(string $url, string $login, string $password): int
{
return 1001;
}
}
В некоторых таких моках, я добавляю возможность динамически подменять значения, чтобы имитировать разные ответы стороннего сервиса.
После я добавляю в
services_test.yaml
соответствующую запись:
services:
_defaults:
autowire: true
App\Service\EnterpriseDispatcherService:
public: true
class: App\Tests\Mock\EnterpriseDispatcherServiceMock
В итоге код разрастается просто катастрофически, если мне нужно протестировать 20 сервисов, мне нужно создать 20 интерфейсов, 20 мок классов и указать 20 записей в
services_test.yaml.
Есть ли проще решения или функциональные тесты — это кроличья нора, в которую лучше не лезь?