0

I'm trying to write a PHPUnit test in my Symfony project for a Voter class.

class CustomVoter extends Voter
{
private MyCustomRepository $myCustomRepository;

public function __construct(MyCustomRepository $myCustomRepository)
{
    $this->myCustomRepository = $myCustomRepository;
}

protected function supports(
    string $attribute,
           $subject
): bool
{
    if (!in_array($attribute, ['edit])) {
        return false;
    }
    return true;
}

protected function voteOnAttribute(
    string $attribute,
    $subject,
    TokenInterface $token
): bool
{
    $user = $token->getUser();
    if (!$user instanceof UserInterface) {
        return false;
    }

    return $this->myCustomRepository->hasAccess(
        $user->getId(),
        $attribute,
        $subject
    );
  } 
}

I have a problem mocking TokenInterface.

In my test I am returning a user from my test database:

private $entityManager;

protected TokenInterface|MockObject|null $token = null;

public function setUp(): void
{
    $kernel = self::bootKernel();

    $this->entityManager = $kernel->getContainer()
        ->get('doctrine')
        ->getManager();

    $user = $this->entityManager
        ->getRepository(Member::class)
        ->findOneBy(['email' => 'user@test.com']);

   $this->token = $this->getMockBuilder(TokenInterface::class)
        ->disableOriginalConstructor()
        ->disableOriginalClone()
        ->disableArgumentCloning()
        ->disallowMockingUnknownTypes()
        ->getMock();

    $this->token->expects($this->once())
        ->method('getUser')
        ->willReturn($user);
}

And when trying to test the voter:

/**
 * @dataProvider provideCases
 */
public function testVote(array $attributes, string $subject, ?TokenInterface $token, $expectedVote) {
    $this->assertEquals($expectedVote, $this->voter->vote($token, $subject, $attributes));
}

public function provideCases(): \Generator
{

    yield 'user can edit' => [
        ['edit'],
        'my_subject',
        $this->token,
        VoterInterface::ACCESS_GRANTED,
    ];
}

And I get:

Expectation failed for method name is "getUser" when invoked 1 time(s). Method was expected to be called 1 times, actually called 0 times.

What is the case here? When dd($user); I get user object from the database..

yaraw69
  • 43
  • 5

1 Answers1

1

I can't write a comment so I will post the answer. I have a feeling that we need to see your full Voter class code to be able to see what is going on.

I think because of wrong attribute or something the vote() method finish the check earlier (before voteOnAttribute() call) so getUser() is not called in your case.

I will try to edit or delete this answer if I figure out how to help you.

EDIT

I tested your case in my local environment and I can say that you should pass the token directly in test method, not via dataProvider.

    /**
     * @dataProvider provideCases
     */
    public function testVote(array $attributes, string $subject, $expectedVote) {
        $this->assertEquals($expectedVote, $this->voter->vote($this->token, $subject, $attributes));
    }

Explanation:

As we can read from the documentation:

All data providers are executed before both the call to the setUpBeforeClass static method and the first call to the setUp method. Because of that you can't access any variables you create there from within a data provider.

  • Thanks again. I have edited my post. Basically I have few more lines which I have now added. I pass this params to the repository which has query builder searching for the specific values from the database table and returns true or false.. (you can now see it in my updated question post) – yaraw69 Jul 08 '22 at 06:19
  • @yaraw69 please share all code inside Voter class, just copy and paste all Voter class code, not only voteOnAttribute method. All lines from 1 to the end :D – Szymon Szymkowiak Jul 08 '22 at 06:27
  • @yaraw69 please check my edit, maybe will be helpful – Szymon Szymkowiak Jul 08 '22 at 08:13
  • Million thanks for this. I have read the docs few weeks back and totally forgot about this part. @ SzymonSzymkowiak you were really helpful! – yaraw69 Jul 08 '22 at 08:34
  • An exception occurred in the driver: could not find driver @SzymonSzymkowiak – yaraw69 Jul 08 '22 at 14:45
  • Can you elaborate? @yaraw69 – Szymon Szymkowiak Jul 08 '22 at 16:28
  • Sorry for that. Managed to solve it. Thank you for the help Really appreciated. @SzymonSzymkowiak – yaraw69 Jul 09 '22 at 12:49
  • Implementing the way you proposed I get: testVote with data set "member can edit" (array('edit'), 'customers', 1) Failed asserting that -1 matches expected 1. What can I do to fix this? @SzymonSzymkowiak – yaraw69 Jul 09 '22 at 13:01
  • @yaraw69 it is hard to determine what is going on with that short description. If you have another problem maybe its better to open new question with full code and description – Szymon Szymkowiak Jul 09 '22 at 19:36