Nimut / testing-framework

TYPO3 testing framework that provides base classes and configuration for PHPUnit tests
GNU General Public License v2.0
52 stars 25 forks source link

ViewHelper Test fails with `count(): Parameter must be an array or an object that implements Countable` with TYPO3 8.7.28 and PHP 7.2 #121

Closed t3easy closed 4 years ago

t3easy commented 4 years ago

The test of my ViewHelper fails because of the following PHP Warning with PHP 7.2 (seven times the same for each set of data):

7) T3easy\Mycontent\Tests\Unit\ViewHelpers\Format\Json\EncodeViewHelperTest::renderConvertsAValue with data set #6 (array(array('Hello world!', 'Foo'), 'Hello world!', 'Bar'), '{"stringValue2":"Bar"}')
count(): Parameter must be an array or an object that implements Countable

/app/.Build/public/typo3/sysext/fluid/Classes/Core/ViewHelper/AbstractViewHelper.php:119
/app/.Build/public/typo3/sysext/fluid/Classes/Core/ViewHelper/AbstractViewHelper.php:162
/app/.Build/vendor/nimut/testing-framework/src/TestingFramework/TestCase/ViewHelperBaseTestcase.php:164
/app/Tests/Unit/ViewHelpers/Format/Json/EncodeViewHelperTest.php:124

If I run the Unit tests of the Core ViewHelper, vendor/phpunit/phpunit/phpunit -c vendor/typo3/testing-framework/Resources/Core/Build/UnitTests.xml typo3/sysext/fluid/Tests/Unit/ in the same environment, I don't get that PHP warning.

It seems like a problem with the reflection: https://github.com/TYPO3/TYPO3.CMS/blob/v8.7.28/typo3/sysext/fluid/Classes/Core/ViewHelper/AbstractViewHelper.php#L118-L121

Did I miss to bootstrap something?

My ViewHelper:

<?php
namespace T3easy\Mycontent\ViewHelpers\Format\Json;

use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic;

class EncodeViewHelper extends AbstractViewHelper
{
    use CompileWithContentArgumentAndRenderStatic;

    const TYPES_PATH = '_types';
    const DEFAULTS_PATH = '_defaults';

    /**
     * @var bool
     */
    protected $escapeChildren = false;

    /**
     * @var boolean
     */
    protected $escapeOutput = false;

    /**
     * Initialize arguments
     */
    public function initializeArguments()
    {
        $this->registerArgument('value', 'mixed', 'The incoming data to convert, or null if VH children should be used');
    }

    /**
     * Applies json_encode() on the specified value.
     *
     * @param array $arguments
     * @param \Closure $renderChildrenClosure
     * @param RenderingContextInterface $renderingContext
     * @return string
     * @see http://www.php.net/manual/en/function.json-encode.php
     */
    public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
    {
        $value = self::prepareValue($renderChildrenClosure());

        return json_encode($value, JSON_FORCE_OBJECT);
    }

    /**
     * @param mixed $value
     * @return mixed
     */
    protected static function prepareValue($value)
    {
        $types = [];
        if (is_array($value)) {
            if (array_key_exists(self::TYPES_PATH, $value) && is_array($value[self::TYPES_PATH])) {
                $types = $value[self::TYPES_PATH];
                unset($value[self::TYPES_PATH]);

                foreach ($types as $key => $type) {
                    if (isset($value[$key])) {
                        settype($value[$key], $type);
                    }
                }
            }

            if (array_key_exists(self::DEFAULTS_PATH, $value) && is_array($value[self::DEFAULTS_PATH])) {
                $defaults = $value[self::DEFAULTS_PATH];
                unset($value[self::DEFAULTS_PATH]);
                foreach ($defaults as $key => $default) {
                    if (isset($types[$key])) {
                        settype($defaults[$key], $types[$key]);
                    }
                    if ($value[$key] === $defaults[$key]) {
                        unset($value[$key]);
                    }
                }
            }
        }

        return $value;
    }
}

The Test

<?php
namespace T3easy\Mycontent\Tests\Unit\ViewHelpers\Format\Json;

use T3easy\Mycontent\ViewHelpers\Format\Json\EncodeViewHelper;
use Nimut\TestingFramework\TestCase\ViewHelperBaseTestcase;
use PHPUnit\Framework\MockObject\MockObject;

class EncodeViewHelperTest extends ViewHelperBaseTestcase
{
    /**
     * @var EncodeViewHelper|MockObject
     */
    protected $viewHelper;

    /**
     * @return void
     */
    protected function setUp()
    {
        parent::setUp();
        $this->viewHelper = $this->getMockBuilder(EncodeViewHelper::class)->setMethods(['renderChildren'])->getMock();
        $this->injectDependenciesIntoViewHelper($this->viewHelper);
    }

    /**
     * @return array
     */
    public function valueDataProvider()
    {
        return [
            [
                'value' => [
                    'testString' => 'Hello world!',
                    'testString2' => 'TYPO3 rocks!',
                ],
                'expected' => '{"testString":"Hello world!","testString2":"TYPO3 rocks!"}',
            ],
            [
                'value' => [],
                'expected' => '{}',
            ],
            [
                'value' => [
                    '_types' => [
                        'boolValue' => 'bool',
                    ],
                    'boolValue' => '1',
                ],
                'expected' => '{"boolValue":true}',
            ],
            [
                'value' => [
                    '_defaults' => [
                        'boolValue' => '0',
                    ],
                    '_types' => [
                        'boolValue' => 'bool',
                    ],
                    'boolValue' => '1',
                ],
                'expected' => '{"boolValue":true}',
            ],
            [
                'value' => [
                    '_types' => [
                        'boolValue' => 'bool',
                        'intValue' => 'int',
                    ],
                    '_defaults' => [
                        'boolValue' => '0',
                        'intValue' => '3000',
                    ],
                    'boolValue' => '1',
                    'intValue' => '4000',
                ],
                'expected' => '{"boolValue":true,"intValue":4000}',
            ],
            [
                'value' => [
                    '_types' => [
                        'boolValue' => 'bool',
                        'intValue' => 'int',
                        'equalStringValue' => 'string',
                        'deviatingStringValue' => 'string',
                    ],
                    '_defaults' => [
                        'boolValue' => '0',
                        'intValue' => '3000',
                        'equalStringValue' => 'TYPO3 rocks!',
                        'deviatingStringValue' => 'foo',
                    ],
                    'boolValue' => '1',
                    'intValue' => '3000',
                    'equalStringValue' => 'TYPO3 rocks!',
                    'deviatingStringValue' => 'bar',
                ],
                'expected' => '{"boolValue":true,"deviatingStringValue":"bar"}',
            ],
            [
                'value' => [
                    '_defaults' => [
                        'stringValue1' => 'Hello world!',
                        'stringValue2' => 'Foo',
                    ],
                    'stringValue1' => 'Hello world!',
                    'stringValue2' => 'Bar',
                ],
                'expected' => '{"stringValue2":"Bar"}',
            ],
        ];
    }

    /**
     * @param mixed $value
     * @param string $expected
     * @test
     * @dataProvider valueDataProvider
     */
    public function renderConvertsAValue($value, $expected)
    {
        $this->setArgumentsUnderTest(
            $this->viewHelper,
            [
                'value' => $value,
            ]
        );
        $actualResult = $this->viewHelper->initializeArgumentsAndRender();
        $this->assertEquals($expected, $actualResult);
    }
}
t3easy commented 4 years ago

I moved one of the core viewhelpers and its test to my extension, changed the namespace and switched from TYPO3\TestingFramework\Fluid\Unit\ViewHelpers\ViewHelperBaseTestcase to Nimut\TestingFramework\TestCase\ViewHelperBaseTestcase and I get the same warning in that tests.

IchHabRecht commented 4 years ago

Resolved with #122