S
S
Sergey Ilichev2022-02-16 21:48:47
PHP
Sergey Ilichev, 2022-02-16 21:48:47

How to test code where functions basically don't return anything?

Hello everyone, I have such a piece of code, who can tell on its example how to test this? Here all the functions do not return anything, but simply do something (add records to the database, send requests to the api, send a message to the queue)

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 is the message handler code from the queue.

$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()));


I'm not good at testing at all. I watched different videos, read the documentation, until I understand how to check such code. I understand to check assert that the function returned something, but here the functions do not return anything. There is no point in mocking objects through $this->createMock either, since in fact then all methods will be empty, and in order to check the performance of such code, in fact, you need to

1. Create a test database and run migrations.
2. Create a message object with your data, simply through new.
3. Pass it to the $this->ofdTicketService->create($ofdTicketMessage) method of the real, non-locked service, so that it actually saves it to the test database.
4. I don’t know further, but maybe there are some methods like assertExistsInDatabaseTable.
5. $this->orangeApiOfdTicketService->createTicket($ofdTicketMessage) calling api is not at all clear how to test in this case, and is it necessary at all? In fact, it can be considered correct operation if this method does not throw an exception.
6. Check if the status has been updated in the database.
7. Sending to the queue is not clear.

I would be grateful if you could help me figure it out. It is possible here, it is possible in a conf, we can agree on a PM.

Answer the question

In order to leave comments, you need to log in

2 answer(s)
A
Anton Shamanov, 2022-02-16
@SilenceOfWinter

you can test the method for exceptions + check the properties that it changes + check uh in English this is a backside effect i.e. Introducing changes that occurred as a result of the plugin execution.
in general, it is not always possible to write a test, for example, you cannot test mail(). It is impossible to check whether the letter has reached the addressee.

D
Dmitry Gordinskiy, 2022-02-16
@DmitriyGordinskiy

If we are talking about Unit Testing, lock down all the dependencies and go through all the scenarios making sure everything goes according to plan. The essence of mocks is that you can check what will come to empty methods, specify what they will return, or, if necessary, throw an exception.
If you want to check what will eventually be saved to the database, this will be closer to EndToEnd testing. In this case, you can raise the test database and fill it with all the necessary data, and for checks, describe your Asserts that will go to the database and check what is there.
It should be borne in mind that if the project is actively developing, it will be natural for it to go through periodic refactorings, which will most likely force you to rewrite Unit tests. Functional and EndToEnd tests, on the other hand, allow you to refactor everything you can, update dependencies and PHP as soon as stable versions are released, and at the same time be sure that the scripts you are testing still work as you intended.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question