Skip to content

Add test for mock model policy resolution and refactor `getRepository…#306

Closed
mirko-pagliai wants to merge 1 commit intocakephp:3.xfrom
mirko-pagliai:3.x-orm-resolver-with-mock-model
Closed

Add test for mock model policy resolution and refactor `getRepository…#306
mirko-pagliai wants to merge 1 commit intocakephp:3.xfrom
mirko-pagliai:3.x-orm-resolver-with-mock-model

Conversation

@mirko-pagliai
Copy link
Copy Markdown

This was not possible:

        $articles = $this->getMockForModel('Articles');
        $resolver = new OrmResolver('TestApp');
        $policy = $resolver->getPolicy($articles->find());

because it caused:

Authorization\Policy\Exception\MissingPolicyException: Policy for `MockObject_ArticlesTable_23778e41` has not been defined.
/home/mirko/Libs/authorization/src/Policy/OrmResolver.php:164
/home/mirko/Libs/authorization/src/Policy/OrmResolver.php:135
/home/mirko/Libs/authorization/src/Policy/OrmResolver.php:93
/home/mirko/Libs/authorization/tests/TestCase/Policy/OrmResolverTest.php:122

In other words, using a mock, with get_class($table) it was impossible to determine the corresponding policy.
Using $table->getAlias(). 'Table' not only is the thing simplifies, but the mock of a model can be used.

I noticed that this can be useful for testing the Policy Scopes, not being able to use the actual model.

Example:

class NotificationsTablePolicy
{
    public function scopeIndex(User $Identity, SelectQuery $Query): SelectQuery
    {
        return $Query->find('InnerJoinWithUserId', $User->id);
    }
}

A mock can now be used and expect that the find() method is called with those arguments.

@mirko-pagliai
Copy link
Copy Markdown
Author

Instead it does not work, because in any case that get_class() is still necessary, and the instance of a mock will be something like Mockobject_Notificationstable_e144e823.

Do something like:

if ($table instanceof MockObject){
}

It would be a bad idea, because it would require phpunit as a dependence in production.

Instead, this works:

if (!str_ends_with($class, 'Table') && str_ends_with(get_parent_class($table) ?: '', 'Table')) {
   $class = get_parent_class($table);
}

But obviously as an idea is definitely bad, I say it first.

Perhaps this implementation is not possible in a simple way.
For now I close, waiting to see if more comes to my mind.

@mirko-pagliai
Copy link
Copy Markdown
Author

mirko-pagliai commented Jun 10, 2025

In the end, the idea was good (allowing to solve the policy also starting from mocks). However, in hindsight the implementation, in addition to being complex, would be very "questionable".

After writing some code, I would say that the best solution is to intervene with a mock of the query (rather than the table).

So (I'm posting the solution, in case it's useful to someone else, even though the purpose of a repository is not to offer a solution, but as an alternative to PR)

class NotificationsTablePolicy {
    public function scopeIndex(User $Identity, SelectQuery $Query): SelectQuery
    {
        return $Query->find('InnerJoinWithUserId', $Identity->id);
    }
}
    public function testScopeIndex(): void
    {
        $Query = Mockery::mock(SelectQuery::class . '[find]', [$this->fetchTable('Notifications')]);
        $Query
            ->shouldReceive('find')
            ->once()
            ->with('InnerJoinWithUserId', 1)
            ->andReturnSelf();

        $this->User->applyScope('index', $Query);
    }

$this->User is obviously set elsewhere.
Of course you could let a real query be created and make assertions on the generated sql, but this was the case where this is not convenient and it is better to verify the call to find().

PR to be considered resolved as closed.

@mirko-pagliai mirko-pagliai deleted the 3.x-orm-resolver-with-mock-model branch June 10, 2025 11:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant