Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

9.1 Add FixtureFactory::createNodeOfType for unit tests. #4317

Open
mhsdesign opened this issue Jun 9, 2023 · 7 comments
Open

9.1 Add FixtureFactory::createNodeOfType for unit tests. #4317

mhsdesign opened this issue Jun 9, 2023 · 7 comments
Labels
9.0 Backlog A category for things that are okay to be rather 'hidden'

Comments

@mhsdesign
Copy link
Member

mhsdesign commented Jun 9, 2023

we were discussing how to stub nodes in tests https://neos-project.slack.com/archives/C3MCBK6S2/p1686294507655429

and it was noted that we need some kind of factory for this:

if the input for the test is a Node, I'd just instantiate one, if the output that is to be tested is a node, then use commands in behat

imho the CR should provide something like FixtureFactory::createNodeOfType for unit tests, then you don't have to deal with that in every test

@mhsdesign mhsdesign added the 9.0 label Jun 10, 2023
@mhsdesign mhsdesign changed the title 9.0 Declare Node constructor as internal? And add FixtureFactory::createNodeOfType for unit tests. 9.0 Add FixtureFactory::createNodeOfType for unit tests. Jun 22, 2023
@mhsdesign
Copy link
Member Author

I currently use this node mock trait

<?php
declare(strict_types=1)

namespace Foo\Bar;

use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint;
use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint;
use Neos\ContentRepository\Core\Factory\ContentRepositoryId;
use Neos\ContentRepository\Core\Feature\NodeModification\Dto\SerializedPropertyValues;
use Neos\ContentRepository\Core\NodeType\NodeType;
use Neos\ContentRepository\Core\NodeType\NodeTypeName;
use Neos\ContentRepository\Core\Projection\ContentGraph\ContentSubgraphIdentity;
use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
use Neos\ContentRepository\Core\Projection\ContentGraph\PropertyCollectionInterface;
use Neos\ContentRepository\Core\Projection\ContentGraph\Timestamps;
use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints;
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateClassification;
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId;
use Neos\ContentRepository\Core\SharedModel\Node\NodeName;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
use PHPUnit\Framework\MockObject\MockBuilder;

/**
 * @method MockBuilder getMockBuilder(string $className)
 */
trait NodeMockTrait
{
    private function createNodeMock(NodeAggregateId $nodeAggregateId = null, array $properties = []): Node
    {
        $propertyCollection = new class ($properties) extends \ArrayObject implements PropertyCollectionInterface {
            public function serialized(): SerializedPropertyValues
            {
                throw new \BadMethodCallException(sprintf('Method %s, not supposed to be called.', __METHOD__));
            }
        };

        return new Node(
            ContentSubgraphIdentity::create(
                ContentRepositoryId::fromString("cr"),
                ContentStreamId::fromString("cs"),
                DimensionSpacePoint::fromArray([]),
                VisibilityConstraints::withoutRestrictions()
            ),
            $nodeAggregateId ?? NodeAggregateId::fromString("na"),
            OriginDimensionSpacePoint::fromArray([]),
            NodeAggregateClassification::CLASSIFICATION_REGULAR,
            NodeTypeName::fromString("nt"),
            $this->getMockBuilder(NodeType::class)->disableOriginalConstructor()->getMock(),
            $propertyCollection,
            NodeName::fromString("nn"),
            Timestamps::create($now = new \DateTimeImmutable(), $now, null, null)
        );
    }
}

@bwaidelich
Copy link
Member

Nice!
And the fact that behat tests are very useful does not mean that we should not at mock helpers like this.

I just wonder: Why a trait and not some public static method?

@mhsdesign
Copy link
Member Author

I just wonder: Why a trait and not some public static method?

No thats just what i use, because i was LAZY ^^ Ofc a factory would be great. (But please inside the normal classes folder)

@mficzel
Copy link
Member

mficzel commented Jun 22, 2023

I want that, where can I get that. The nodeAcess package has tons of tests that are disabled mainly because of that basically.

@mhsdesign
Copy link
Member Author

mhsdesign commented Jul 10, 2023

@grebaldi and me wondered if there should be a dummy fixture for the whole CR as well?

<?php

namespace Foo\Bar;

use Neos\ContentRepository\Core\CommandHandler\CommandBus;
use Neos\ContentRepository\Core\ContentRepository;
use Neos\ContentRepository\Core\Dimension\ContentDimensionSourceInterface;
use Neos\ContentRepository\Core\DimensionSpace\ContentDimensionZookeeper;
use Neos\ContentRepository\Core\DimensionSpace\InterDimensionalVariationGraph;
use Neos\ContentRepository\Core\EventStore\EventNormalizer;
use Neos\ContentRepository\Core\EventStore\EventPersister;
use Neos\ContentRepository\Core\NodeType\DefaultNodeLabelGeneratorFactory;
use Neos\ContentRepository\Core\NodeType\NodeTypeManager;
use Neos\ContentRepository\Core\Projection\ProjectionCatchUpTriggerInterface;
use Neos\ContentRepository\Core\Projection\Projections;
use Neos\ContentRepository\Core\SharedModel\User\UserIdProviderInterface;
use Neos\EventStore\EventStoreInterface;
use PHPUnit\Framework\MockObject\MockBuilder;
use Psr\Clock\ClockInterface;

/**
 * @method MockBuilder getMockBuilder(string $className)
 */
trait ContentRepositoryMockTrait
{
    private function createContentRepositoryMock(): ContentRepository
    {
        return new ContentRepository(
            new CommandBus(),
            $es = $this->getMockBuilder(EventStoreInterface::class)->getMock(),
            $p = Projections::create(),
            new EventPersister(
                $es,
                $this->getMockBuilder(ProjectionCatchUpTriggerInterface::class)->getMock(),
                new EventNormalizer(),
                $p
            ),
            new NodeTypeManager(
                fn () => [],
                new DefaultNodeLabelGeneratorFactory(),
                null
            ),
            new InterDimensionalVariationGraph(
                $cds = $this->getMockBuilder(ContentDimensionSourceInterface::class)->getMock(),
                new ContentDimensionZookeeper(
                    $cds
                )
            ),
            $cds,
            $this->getMockBuilder(UserIdProviderInterface::class)->getMock(),
            $this->getMockBuilder(ClockInterface::class)->getMock()
        );
    }
}

@nezaniel
Copy link
Member

Something similar is being prepared in #4455

@nezaniel nezaniel moved this from Todo to Under Review 👀 in Neos 9.0 Release Board Aug 29, 2023
@mhsdesign mhsdesign moved this from Under Review 👀 to Todo in Neos 9.0 Release Board Sep 5, 2023
@mhsdesign mhsdesign removed the 9.0beta1 label Sep 5, 2023
bwaidelich added a commit that referenced this issue Sep 14, 2023
Removes the `PropertyCollectionInterface` in order to achieve
a more reliable behavior and not to give the impression of extension
points that are actually not extensible.

**Note:** This change disables some functional `NodeHelperTest` because
there is currently no easy way to mock the `Node` read model.
We'll address this with #4317

Resolves: #4464
@mhsdesign mhsdesign moved this from Todo to In Progress 🚧 in Neos 9.0 Release Board Sep 18, 2023
@mhsdesign
Copy link
Member Author

With #4513 the properties are not as easy as mock able as before:

For a mock helper i like to have an option to tell the node that these are its properties in a simple array format.

before:

        $properties = ['foo' => "bar"];

        $propertyCollection = new class ($properties) extends \ArrayObject implements PropertyCollectionInterface {
            public function serialized(): SerializedPropertyValues
            {
                throw new \BadMethodCallException(sprintf('Method %s, not supposed to be called.', __METHOD__));
            }
        };

after:

$textNodeProperties = new PropertyCollection(
    SerializedPropertyValues::fromArray([
        'foo' => new SerializedPropertyValue('bar', 'string')
    ]),
    new PropertyConverter(
        new Serializer([
            new DateTimeNormalizer(),
            new ScalarNormalizer(),
            new BackedEnumNormalizer(),
            new ArrayNormalizer(),
            new UriNormalizer(),
            new ValueObjectArrayDenormalizer(),
            new ValueObjectBoolDenormalizer(),
            new ValueObjectFloatDenormalizer(),
            new ValueObjectIntDenormalizer(),
            new ValueObjectStringDenormalizer(),
            new CollectionTypeDenormalizer()
        ])
    )
);

i also experimented with using the serializer to archive this (ideally the type for the serialization should be resolved from the actual current type):

    public function createNode(
         ?NodeAggregateId $nodeAggregateId = null,
         SerializedPropertyValues|array|null $propertyValues = null,
     ): Node {
         if (is_array($propertyValues)) {
             $propertyValues = $this->propertyConverter->serializePropertyValues(
                 PropertyValuesToWrite::fromArray($propertyValues), // hacky as we are using this NodeModification command dto model
                 $nodeType // i also want to pass the nodeType here hacky, as it needs to have the correct property type defnitions
             );
         }
      # ...

      return new Node($propertyValues);

@ahaeslich ahaeslich moved this from In Progress 🚧 to Todo in Neos 9.0 Release Board Nov 10, 2023
@mhsdesign mhsdesign moved this from Todo to Low Priority in Neos 9.0 Release Board Nov 10, 2023
@mhsdesign mhsdesign added the 9.1 label Feb 7, 2024
@mhsdesign mhsdesign changed the title 9.0 Add FixtureFactory::createNodeOfType for unit tests. 9.1 Add FixtureFactory::createNodeOfType for unit tests. Feb 7, 2024
@mhsdesign mhsdesign removed the 9.0 label Feb 8, 2024
@mhsdesign mhsdesign added 9.0 Backlog A category for things that are okay to be rather 'hidden' and removed 9.1 labels Oct 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
9.0 Backlog A category for things that are okay to be rather 'hidden'
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants