Почему returnValueMap () возвращает NULL - PullRequest
1 голос
/ 12 мая 2019

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

Я смоделировал две сущности, а затем попытался смоделировать их хранилище с заданной картой возвращаемых значений. Тест не пройден, и отладка показывает, что 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));
    }
}

Ответы [ 2 ]

1 голос
/ 12 мая 2019

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

Метод 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;
    }

Я подозревал, что метод всегда возвращался с нулем из-за невыполненного условия $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],
            ]);
1 голос
/ 12 мая 2019

Похоже, что для параметра 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()
    {}
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...