first-programmer
@first-programmer
Backend software engineer

Как тестировать код, где функции в основном ничего не возвращают?

Всем привет, есть у меня такой кусок кода, кто может подсказать на его примере, как вообще такое тестировать? Тут все функции ничего не возвращают, а просто что-то выполняют (добавляют в базу данных записи, отправляют запросы в апи, отправляют сообщение в очередь)

final class PartnerApiOfdTicketHandler implements MessageHandlerInterface
{
    private OfdTicketMessage $ofdTicketMessage;
    private OfdTicketService $ofdTicketService;
    private OrangeApiOfdTicketService $orangeApiOfdTicketService;
    private OfdTicketStatusProducer $ofdTicketStatusProducer;
    private LoggerInterface $logger;

    public function __construct(
        OfdTicketService $ofdTicketService,
        OrangeApiOfdTicketService $orangeApiOfdTicketService,
        OfdTicketStatusProducer $ofdTicketStatusProducer,
        LoggerInterface $logger
    ) {
        $this->ofdTicketService = $ofdTicketService;
        $this->orangeApiOfdTicketService = $orangeApiOfdTicketService;
        $this->ofdTicketStatusProducer = $ofdTicketStatusProducer;
        $this->logger = $logger;
    }

    public function __invoke(OfdTicketMessage $ofdTicketMessage): void
    {
        $this->logger->info(
            'OfdTicketMessage was received',
            ['data' => $ofdTicketMessage->toArray()]
        );

        $this->ofdTicketMessage = $ofdTicketMessage;

        try {
            $this->ofdTicketService->create($ofdTicketMessage);
            $this->orangeApiOfdTicketService->createTicket($ofdTicketMessage);

            $this->ofdTicketService->update(
                $ofdTicketMessage,
                [
                    'processed' => true,
                    'statusCode' => OrangeApiResponseStatus::TICKET_CREATED
                ]
            );

            $this->ofdTicketStatusProducer->push(new OfdTicketStatusMessage($ofdTicketMessage->toArray()));
        } catch (ConflictException $e) {
            $this->handleException($e, 'Need to ack message because: ');

            throw new  UnrecoverableMessageHandlingException($e->getMessage(), $e->getCode());
        } catch (OfdOrangeApiException $e) {
            $this->handleException($e, 'Ofd orange api error: ', [
                'error' => 'Ofd orange api error: ' . $e->getMessage(),
                'statusCode' => $e->getCode()
            ]);
        } catch (TransportExceptionInterface $e) {
            $this->handleException($e, 'Can\'t send request to ofd orange api: ', [
                'error' => 'Can\'t send request to ofd orange api: ' . $e->getMessage(),
                'statusCode' => $e->getCode()
            ]);
        } catch (\Exception|\Throwable $e) {
            $this->handleException($e, 'Something went wrong: ', [
                'error' => 'Something went wrong: ' . $e->getMessage(),
                'statusCode' => InternalExceptionStatus::INTERNAL_ERROR
            ]);

            throw new  UnrecoverableMessageHandlingException($e->getMessage(), $e->getCode());
        }
    }


Это код обработчика сообщений из очереди.

$this->ofdTicketService->create($ofdTicketMessage); - сохранят сообщение в базу данных
 $this->orangeApiOfdTicketService->createTicket($ofdTicketMessage); - создает тикет через стороннее api, тело ответа в положительном сценарии всегда пустое, просто приходит код 201, при негативном возникает исключение и тело ответа с ошибкой.

обновляем статус в базе данных
$this->ofdTicketService->update(
    $ofdTicketMessage,
         [
             'processed' => true,
             'statusCode' => OrangeApiResponseStatus::TICKET_CREATED
         ]
    );

Отправляем в очередь.
$this->ofdTicketStatusProducer->push(new OfdTicketStatusMessage($ofdTicketMessage->toArray()));


Я в тестировании совсем не шарю. Посмотрел разные видео, почитал документацию, пока не понимаю, как проверять такой код. Я понимаю проверить assert что функция вернулся что-то, но тут функции ничего не возвращают. мокать объекты через $this->createMock тоже смысла нет, так как по сути тогда все методы будут пустыми, а чтобы проверить работоспособность такого кода, по сути нужно

1. Создать тестовую базу данных и накатить миграции.
2. Создать объект сообщения со своими данными, просто через new.
3. Передать его в метод настоящего, не замоканого сервиса $this->ofdTicketService->create($ofdTicketMessage), чтобы тот по настоящему сохранил его в тестовую базу данных.
4. Дальше не знаю, но может есть какие-то методы типа assertExistsInDatabaseTable.
5. $this->orangeApiOfdTicketService->createTicket($ofdTicketMessage) обращение к api вообще не понятно, как тестировать именно в этом случае, и нужно ли вообще? По сути правильной работой его можно считать, если этот метод не выбросит исключение.
6. Проверить обновился ли статус в базе данных.
7. С отправкой в очередь не понятно.

Буду благодарен, если поможете разобраться. Можно тут, можно в конфе, можем договориться в лс.
  • Вопрос задан
  • 227 просмотров
Пригласить эксперта
Ответы на вопрос 2
SilenceOfWinter
@SilenceOfWinter Куратор тега PHP
та еще зажигалка...
ты можешь тестировать метод на срабатывание исключений + проверить те свойства что он изменяет + проверить ээ по английски это backside effect т.е. внение изменения произошедшие в результате выполнения плагина.
вообще не всегда можно написать тест, например, нельзя протестировать mail() т.к. нельзя проверить дошло ли письмо до адресата.
Ответ написан
Если речь о Модульном тестировании - замокать всё зависимости и пройтись по всем сценариям убедившись что всё идёт по плану. Суть моков в том что вы можете проверять что будет приходить в пустые методы, указывать что они будут возвращать, или при необходимости, бросать исключение.

Если хотите проверить что в итоге сохранится в базу - это уже будет ближе к EndToEnd тестированию. В таком случае можно поднять тестовую базу, и наполнить всеми необходимыми данными, а для проверок описать свои Assert-ы которые будут ходить в базу и проверять что там лежит.

Следует учитывать что если проект активно развивается - для него будет естественным переживать периодические рефакторинги, что с высокой вероятностью будет вынуждать к переписыванию Unit-тестов. Функциональные и EndToEnd тесты наоборот - позволят рефакторить всё что только можно, обновлять зависимости и PHP как только будут выходить stable версии, и при этом быть уверенным что тестируемые сценарии всё ещё работают как вы задумывали.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы