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' => '[email protected]']);
$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..
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.
Explanation:
As we can read from the documentation: