Почему returnValueMap() возвращает NULL

Пытаясь имитировать репозиторий доктрин внутри теста, returnValueMap() всегда возвращает NULL при использовании с методом найтиOneBy.

Я издевался над двумя объектами, а затем пытался издеваться над их репозиторием с заданной картой возвращаемых значений. Тест не пройден, и отладка показывает, что returnValueMap() возвращает NULL.

Вот тестируемый класс (денормализатор)

<?php

declare(strict_types=1);

namespace App\Serializer;

use App\Entity\AdditionalService;
use App\Repository\AdditionalServiceRepository;
use Dto\AdditionalServiceCollection;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;

class AdditionalServiceCollectionDenormalizer implements DenormalizerInterface
{
    /** @var AdditionalServiceRepository */
    private $additionalServiceRepository;

    public function __construct(AdditionalServiceRepository $additionalServiceRepository)
    {
        $this->additionalServiceRepository = $additionalServiceRepository;
    }

    public function denormalize($mappedCsvRow, $class, $format = null, array $context = [])
    {
        $addtionalServicesCollection = new AdditionalServiceCollection();
        foreach ($mappedCsvRow as $fieldName => $fieldValue) {
            /** @var AdditionalService $additionalService */
            $additionalService = $this->additionalServiceRepository->findOneBy(['name'=>$fieldName]);

            if ($additionalService) {
                $addtionalServicesCollection->add($additionalService->getId(), $fieldValue);
            }
        }

        return $addtionalServicesCollection;
    }

    public function supportsDenormalization($data, $type, $format = null)
    {
        return $type instanceof  AdditionalServiceCollection;
    }
}

Вот мой тестовый класс:

<?php

namespace App\Tests\Import\Config;

use App\Entity\AdditionalService;
use App\Repository\AdditionalServiceRepository;
use App\Serializer\AdditionalServiceCollectionDenormalizer;
use PHPUnit\Framework\TestCase;
use Dto\AdditionalServiceCollection;

class AddionalServiceCollectionDenormalizerTest extends TestCase
{
    public function provider()
    {
        $expected = new AdditionalServiceCollection();
        $expected->add(1, 22.1)->add(2, 3.1);

        return [
            [['man_1' => 22.1], $expected],
            [['recycling' => 3.1], $expected],
        ];
    }

    /**
     * @dataProvider provider
     * @covers \App\Serializer\AdditionalServiceCollectionDenormalizer::denormalize
     */
    public function testDenormalize(array $row, AdditionalServiceCollection $exptected)
    {
        $manOneService = $this->createMock(AdditionalService::class);
        $manOneService->expects($this->any())->method('getId')->willReturn(1);

        $recycling = $this->createMock(AdditionalService::class);
        $recycling->expects($this->any())->method('getId')->willReturn(2);

        $additionalServicesRepoMock = $this
            ->getMockBuilder(AdditionalServiceRepository::class)
            ->setMethods(['findOneBy'])
            ->disableOriginalConstructor()
            ->getMock();
        $additionalServicesRepoMock
            ->expects($this->any())
            ->method('findOneBy')
            ->will($this->returnValueMap(
                [
                    ['name'=>['man_1'], $manOneService],
                    ['name'=>['recycling'], $recycling],
                ]
            ));

        $denormalizer = new AdditionalServiceCollectionDenormalizer($additionalServicesRepoMock);

        self::assertEquals($exptected, $denormalizer->denormalize($row, AdditionalServiceCollection::class));
    }
}

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Symfony Station Communiqué - 17 февраля 2023 г
Symfony Station Communiqué - 17 февраля 2023 г
Это коммюнике первоначально появилось на Symfony Station , вашем источнике передовых новостей Symfony, PHP и кибербезопасности.
Управление ответами api для исключений на Symfony с помощью KernelEvents
Управление ответами api для исключений на Symfony с помощью KernelEvents
Много раз при создании api нам нужно возвращать клиентам разные ответы в зависимости от возникшего исключения.
1
0
1 050
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Похоже, параметр returnValueMap() в testDenormalize() нуждается в квадратных скобках, чтобы сделать его индексированным массивом.

Вот слегка измененная версия фрагмента кода из документ PHPUnit:

<?php

namespace App\Tests;

use PHPUnit\Framework\TestCase;

class ReturnValueMapTest extends TestCase
{
    public function testReturnValueMapWithAssociativeArray()
    {
        $stub = $this->createMock(SomeClass::class);

        $map = [
            [
                'name' => ['man_1'],
                'Hello'
            ],
        ];

        $stub->method('doSomething')
            ->will($this->returnValueMap($map));

        // This will fail as doSomething() returns null
        $this->assertSame('Hello', $stub->doSomething(['name' => ['man_1']]));
    }

    public function testReturnValueMapWithIndexedArray()
    {
        $stub = $this->createMock(SomeClass::class);

        $map = [
            [
                ['name' => ['man_1']], // Notice the difference
                'Hello'
            ],
        ];

        $stub->method('doSomething')
            ->will($this->returnValueMap($map));

        $this->assertSame('Hello', $stub->doSomething(['name' => ['man_1']]));
    }
}

class SomeClass
{
    public function doSomething()
    {}
}

Я попробовал решение, но оно не работает. На самом деле метод findOneBy() принимает в качестве параметра ассоциативный массив, а не индексированный.

medunes 12.05.2019 18:36

Я имел в виду, что returnValueMap() должен принимать индексированный массив. Хотя я подтвердил, что public function doSomething($parameterA, $parameterB = null) также сломается testReturnValueMapWithIndexedArray(). Должен сказать, это не суперинтуитивно. Предположим, что findOneBy() взято из Doctrine.

Nobu 13.05.2019 15:30
Ответ принят как подходящий

Мне было трудно отлаживать библиотеку PHPUnit, чтобы наконец понять, что это метод findOneBy(), который ожидает два аргумента, среди которых второй является необязательным (установленным в null)

Метод willReturnMap() выглядит следующим образом:

/**
 * Stubs a method by returning a value from a map.
 */
class ReturnValueMap implements Stub
{
    /**
     * @var array
     */
    private $valueMap;

    public function __construct(array $valueMap)
    {
        $this->valueMap = $valueMap;
    }

    public function invoke(Invocation $invocation)
    {
        $parameterCount = \count($invocation->getParameters());

        foreach ($this->valueMap as $map) {
            if (!\is_array($map) || $parameterCount !== (\count($map) - 1)) {
                continue;
            }

            $return = \array_pop($map);

            if ($invocation->getParameters() === $map) {
                return $return;
            }
        }

        return;
    }

Я подозревал, что метод всегда возвращает значение null из-за невыполненного условия $parameterCount !== (\count($map) - 1). Точка останова подтвердила мои сомнения, а также показала, что дамп $invocation->getParameters() выглядит следующим образом:

array(2) {
  [0] =>
  array(1) {
    'name' =>
    string(5) "man_1"
  }
  [1] =>
  NULL
}

Следовательно, мне пришлось явно передать null в качестве второго аргумента. Итак, наконец, рабочая карта должна была быть:

$this->additionalServicesRepoMock
            ->method('findOneBy')
            ->willReturnMap([
                [['name' => 'man_1'], null, $manOneService],
                [['name' => 'recycling'], null, $recyclingService],
            ]);

Другие вопросы по теме