public function create(Request $request, Create\Handler $handler): Response
final readonly class Handler
{
public function __construct(
private EnterpriseDispatcherRepository $enterpriseDispatcherRepository,
private Flusher $flusher,
private EnterpriseDispatcherService $enterpriseDispatcherService,
private CustomHttpClient $customHttpClient,
) {}
public function handle(Command $command): void
{
dd($this->enterpriseDispatcherService->getEnterpriseDispatcherRealUserId($command->url, $command->login, $command->password));
}
$client->getContainer()->get(EnterpriseDispatcherService::class)
, то вернёт нужно значение, но как только уходит в контроллер, он использует настоящие классы, игнорируя те, что я замокал. public function testCreate(): void
{
$client = self::createClient();
$service = $this->createMock(EnterpriseDispatcherService::class);
$service->expects($this->once())
->method('getEnterpriseDispatcherRealUserId')
->willReturn($userId = 12333);
self::getContainer()->set(EnterpriseDispatcherService::class, $service);
$this->loginAs('test_admin');
$client->request('GET', '/enterprise-dispatchers/create');
$crawler = $client->submitForm('Create', [
'form[name]' => $name = 'Test Enterprise Dispatcher',
'form[url]' => 'http://127.0.0.2:9003',
'form[login]' => 'test_login',
'form[password]' => 'test_password',
]);
}
$client->submitForm()
, оно должен вернуть 12333, но оно упадёт с ошибкой, потому что будет использовать оригинальный класс. setUp()
запускается перед каждым тестом, то есть не важно вынесу ли я эти значения непосредственно в тест или оставлю в setUp()
. В итоге код разрастается просто катастрофически, если мне нужно протестировать 20 сервисов, мне нужно создать 20 интерфейсов, 20 мок классов и указать 20 записей в services_test.yaml.
final class CreateTest extends DbWebTestCase
{
public function testCreate(): void
{
$this->loginAs('test_admin');
$this->client->request('GET', '/panel/solidcp/enterprise-dispatchers/create');
//тут динамическая подмена заголовка HTTP по сути это тоже касается вопроса,
//так как мокание происходит похожим образом через *.yaml
$this->setCustomHttpClientRespond('http://127.0.0.2:9003', ['HTTP/1.1 200 OK']);
$this->client->submitForm('Create', [
'form[name]' => $name = 'Test Enterprise Dispatcher',
'form[url]' => 'http://127.0.0.2:9003',
'form[login]' => 'test_login',
'form[password]' => 'test_password',
]);
$this->assertSame(302, $this->client->getResponse()->getStatusCode());
$crawler = $this->client->followRedirect();
$this->assertSame(200, $this->client->getResponse()->getStatusCode());
$this->assertStringContainsString('Enterprise Dispatchers', $crawler->filter('title')->text());
$this->assertStringContainsString($name, $crawler->filter('body')->text());
}
}
class DbWebTestCase extends WebTestCase
{
private EntityManagerInterface $em;
protected KernelBrowser $client;
protected function setUp(): void
{
parent::setUp();
$this->client = static::createClient();
$this->client->disableReboot();
$this->em = static::$kernel->getContainer()->get('doctrine')->getManager();
$this->em->getConnection()->beginTransaction();
$this->em->getConnection()->setAutoCommit(false);
}
protected function tearDown(): void
{
$this->em->getConnection()->rollback();
$this->em->close();
parent::tearDown();
}
protected function loginAs(string $name): void
{
$userRepository = $this->client->getContainer()->get(UserRepository::class);
$user = $userRepository->getByLogin($name);
$this->client->loginUser(UserMapper::mapUserToUserIdentity($user));
}
}
$this->client->getContainer()->set();
вызовет ошибкуSymfony\Component\DependencyInjection\Exception\InvalidArgumentException:
The "App\Service\Service" service is already initialized, you cannot replace it.
Когда идешь таким путем, то да - получается много компонентов (в данном случае классов/файлов).
Symfony\Component\DependencyInjection\Exception\InvalidArgumentException:
The "App\Service\Service" service is already initialized, you cannot replace it.
container->reset()
чтобы сбросить контейнер зависимостей и избежать ошибку выше, но со всеми подводными камнями.
Добавил disableReboot(), он начал использовать мок классы везде :).