Задать вопрос
@tanuxa2022

Как исправить ошибку 'No expectations were specified' для метода factory в Mockery?

У меня есть определённая логика, которую необходимо покрыть тестами, а именно
public function handle(ConsumerMessage $message): bool
    {
        $action = $message->getAction();

        $mpnIdBrandIdSkuMarketplaceIds = $this->converter->convert($message);

        if (empty($mpnIdBrandIdSkuMarketplaceIds) === false) {
            $mpnIds = ArrayHelper::arrayColumnUnique($mpnIdBrandIdSkuMarketplaceIds, self::MPN_ID_FIELD);

            $this->eventManager->triggerEvent(new NewEventSystemMessageEvent($message, $mpnIds));

            $allowedMpnIds                = $this->allowedFcMpnIdsFilter->filter($mpnIds);
            $allowedMpnIdsIndexedByMpnIds = array_combine($allowedMpnIds, $allowedMpnIds);

            foreach ($mpnIdBrandIdSkuMarketplaceIds as $item) {
                $mpnId         = $item[self::MPN_ID_FIELD];
                $marketplaceId = $item[self::MARKETPLACE_PRODUCT_ID_FIELD];

                if (self::DELETE_ACTION === $action) {
                    if ($marketplaceId !== null) {
                        $this->identifiersBufferByAction[$action][$marketplaceId] = $marketplaceId;
                        $this->marketplaceProductIdToMpnId[$marketplaceId] = $mpnId;
                    }
                } elseif (isset($allowedMpnIdsIndexedByMpnIds[$mpnId])) {
                    $this->identifiersBufferByAction[$action][$mpnId] = $mpnId;
                }
            }
        }

        foreach ($this->identifiersBufferByAction as $action => $identifiers) {
            if ($this->maxBufferSize <= count($identifiers)) {
                $this->innerFlush($action, $identifiers);
            }
        }

        return true;
    }

    /**
     * @throws InvalidArgumentException
     * @throws Exception
     */
    private function innerFlush(string $action, array $identifiers): void
    {
        $ids = array_keys($identifiers);
        if (count($ids) > 0) {
            $isDeleteAction = self::DELETE_ACTION === $action;

            $trData = [];

            if (!$isDeleteAction) {
                foreach ($ids as $mpnProductId) {
                    $trData[] = [
                        ProductQueue::TABLE_COLUMN_MPN_PRODUCT_ID         => $mpnProductId,
                        ProductQueue::TABLE_COLUMN_OPERATION_TYPE         => ProductQueue::ENUM_OPERATION_TYPE_UPDATE,
                        ProductQueue::TABLE_COLUMN_MARKETPLACE_PRODUCT_ID => null
                    ];
                }

                $transaction = $this->insertOnDuplicateKeyUpdateTransactionFactory->factory(
                    ProductQueue::class,
                    $trData,
                    [
                        ProductQueue::TABLE_COLUMN_MPN_PRODUCT_ID
                    ]
                );
            } else {
                foreach ($ids as $marketplaceProductId) {
                    $mpnProductId = $this->marketplaceProductIdToMpnId[$marketplaceProductId] ?? null;
                    if ($mpnProductId !== null) {
                        $trData[] = [
                            ProductQueue::TABLE_COLUMN_MPN_PRODUCT_ID         => $mpnProductId,
                            ProductQueue::TABLE_COLUMN_OPERATION_TYPE         => ProductQueue::ENUM_OPERATION_TYPE_DELETE,
                            ProductQueue::TABLE_COLUMN_MARKETPLACE_PRODUCT_ID => $marketplaceProductId
                        ];
                    }
                }

                $transaction = $this->insertOnDuplicateKeyUpdateTransactionFactory->factory(
                    ProductQueue::class,
                    $trData,
                    [
                        ProductQueue::TABLE_COLUMN_MPN_PRODUCT_ID
                    ]
                );

                $this->marketplaceProductIdToMpnId = [];
            }

            $this->tm->doTransaction($transaction);
        }

        $this->identifiersBufferByAction[$action] = [];
    }


Можно заметить, что метод innerFlush вызывается в том случае, когда буфер полностью заполнен.
Я покрыла данную логику следующим, тестом, а именно для проверки удаления
/**
     * @test
     * @throws Exception
     */
    public function deleteHandleTest(): void
    {
        /** @var ConsumerMessage|MockInterface $message */
        $message = Mockery::mock(ConsumerMessage::class);
        $message->shouldReceive('getAction')->once()->with()->andReturn('delete');

        $converted = [
            [
                'mpn_id' => '11',
                'carid_brand_id' => '111',
                'sku' => 'sku1',
                'marketplace_product_id' => '1',
            ],
            [
                'mpn_id' => '22',
                'carid_brand_id' => '222',
                'sku' => 'sku2',
                'marketplace_product_id' => '2',
            ],
            [
                'mpn_id' => '33',
                'carid_brand_id' => '333',
                'sku' => 'sku3',
                'marketplace_product_id' => '3',
            ],
            [
                'mpn_id' => '44',
                'carid_brand_id' => '444',
                'sku' => 'sku4',
                'marketplace_product_id' => '4',
            ],
        ];

        $this->converter->shouldReceive('convert')->once()
            ->with($message)
            ->andReturn($converted);

        $expectedEvent = new NewEventSystemMessageEvent($message);

        $this->eventManager->shouldReceive('triggerEvent')
            ->once()
            ->with(
                Mockery::on(
                    static function (NewEventSystemMessageEvent $actualEvent) use ($expectedEvent) {
                        return $expectedEvent->getMessage() === $actualEvent->getMessage();
                    }
                )
            );

        $this->allowedFcMpnIdsFilter->shouldReceive('filter')
            ->once()
            ->with(['11', '22', '33', '44'])
            ->andReturn(['11', '22', '33']);

        $this->handler = new EventSystemHandler(
            $this->converter,
            $this->eventManager,
            $this->tm,
            $this->insertOnDuplicateKeyUpdateTransactionFactory,
            $this->allowedFcMpnIdsFilter,
            self::MAX_BUFFER_SIZE
        );

        $result = $this->handler->handle($message);

        self::assertTrue($result, 'The method handle must return true on successful processing.');

        // Проверяем состояние буфера identifiersBufferByAction
        $expectedBuffer = [
            'delete' => [
                '1' => '1',
                '2' => '2',
                '3' => '3'
            ],
        ];

        $reflection = new ReflectionClass($this->handler); //add reflection

        $property = $reflection->getProperty('identifiersBufferByAction');
        $property->setAccessible(true);

        $actualBuffer = $property->getValue($this->handler);

        self::assertEquals(
            $expectedBuffer,
            $actualBuffer,
            'The identifiersBufferByAction buffer must contain only identifiers that pass the filter'
        );

        $expectedMarketplaceProductIdToMpnId = [
            '1' => '11',
            '2' => '22',
            '3' => '33'
        ];

        $reflection = new ReflectionClass($this->handler);

        $property = $reflection->getProperty('marketplaceProductIdToMpnId');
        $property->setAccessible(true);

        $actualMarketplaceProductIdToMpnId = $property->getValue($this->handler);

        self::assertEquals(
            $expectedMarketplaceProductIdToMpnId,
            $actualMarketplaceProductIdToMpnId,
            'The marketplaceProductIdToMpnId array must contain the mapping of marketplace_product_id to mpn_id'
        );

        $trData = [
            [
                ProductQueue::TABLE_COLUMN_MPN_PRODUCT_ID         => '11',
                ProductQueue::TABLE_COLUMN_OPERATION_TYPE         => ProductQueue::ENUM_OPERATION_TYPE_DELETE,
                ProductQueue::TABLE_COLUMN_MARKETPLACE_PRODUCT_ID => '1',
            ],
            [
                ProductQueue::TABLE_COLUMN_MPN_PRODUCT_ID         => '22',
                ProductQueue::TABLE_COLUMN_OPERATION_TYPE         => ProductQueue::ENUM_OPERATION_TYPE_DELETE,
                ProductQueue::TABLE_COLUMN_MARKETPLACE_PRODUCT_ID => '2',
            ],
            [
                ProductQueue::TABLE_COLUMN_MPN_PRODUCT_ID         => '33',
                ProductQueue::TABLE_COLUMN_OPERATION_TYPE         => ProductQueue::ENUM_OPERATION_TYPE_DELETE,
                ProductQueue::TABLE_COLUMN_MARKETPLACE_PRODUCT_ID => '3',
            ]
        ];

        $this->insertOnDuplicateKeyUpdateTransactionFactory->shouldReceive('factory')
            ->once()
            ->with(
                ProductQueue::class,
                $trData,
                [ProductQueue::TABLE_COLUMN_MPN_PRODUCT_ID]
            )
            ->andReturn(); //problem

        $this->tm->shouldNotHaveReceived('doTransaction')->once();
    }

Я не совсем понимаю во-первых, что мне здесь для корректности необходимо вернуть
->andReturn(); //problem

А во-вторых при запуске теста, я вижу следующую ошибку
Mockery\Exception\BadMethodCallException : Received Mockery_3_App_Transactions_Factories_InsertOnDuplicateKeyUpdateTransactionFactory::factory(), but no expectations were specified

Не понимаю уже куда копать и что не так. Прошу помощи
  • Вопрос задан
  • 16 просмотров
Подписаться 1 Средний Комментировать
Пригласить эксперта
Ответы на вопрос 1
Моки и их ожидания нужно описывать до вызова кода который от этих моков зависит.

У вас часть ожиданий описана в самом конце теста, уже после вызова тестируемых методов.
Ответ написан
Комментировать
Ваш ответ на вопрос

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

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