2
votes

I am trying to create a Unit Test to test Symfony 4 code that interacts with the Database.

The method that needs testing contains a call to a custom repository Class and this is causing an error when running phpunit: Error: Call to undefined method Mock_ObjectRepository_583b1688::findLastRegisteredUsers()

I suspect the problem might be with how I'm calling the repository, but not sure how to fix it.

tests/UserTest.php

class UserTest extends TestCase
{
    public function testCalculateTotalUsersReturnsInteger()
    {

        $user = new User();
        $user->setFullname('Test');

        // ! This might be what is causing the problem !
        $userRepository = $this->createMock(ObjectRepository::class); 

        $userRepository->expects($this->any())
            ->method('find')
            ->willReturn($user);

        $objectManager = $this->createMock(ObjectManager::class);

        $objectManager->expects($this->any())
            ->method('getRepository')
            ->willReturn($userRepository);

        $userCalculator = new RegistrationHandler($objectManager);
        $result = $registrationHandler->getAccountManager();

        $this->assertInternalType('int', $result);
    }
}

Repository/UserRepository.php

class UserRepository extends EntityRepository
{

    public function findLastRegisteredUsers($maxResults)
    {
        return $this->createQueryBuilder('user')
            ->andWhere('user.customField IS NOT NULL')
            ->addOrderBy('user.id', 'DESC')
            ->setFirstResult(0)
            ->setMaxResults($maxResults)
            ->getQuery()
            ->execute();
    }
}

src/User/UserCalculator.php

// src/User/UserCalculator.php
namespace App\User;

use Doctrine\Common\Persistence\ObjectManager;

class UserCalculator
{
    private $objectManager;

    public function __construct(ObjectManager $objectManager)
    {
        $this->objectManager = $objectManager;
    }

    public function calculateTotalUsers()
    {
        $userRepository = $this->objectManager
            ->getRepository(User::class);

        $users = $userRepository->findLastRegisteredUsers(100);

        // custom code
    }
}

Just to be clear, the path to the custom EntityRepository class is specified in the User Entity class using the following annotation

/**
 * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
 */

Any ideas how to get the Test to use the custom repository class? I think I might need to change something in $userRepository = $this->createMock(ObjectRepository::class); e.g. using $this->getMockBuilder() ?

1
You are expecting registry method('find'), not findLastRegisteredUsers - Cerad
Ooops, missed that one out :) But now I am getting an error: Trying to configure method "findLastRegisteredUsers" which cannot be configured because it does not exist, has not been specified, is final, or is static - Martin
You need to mock UserRepository not the base ObjectRepository. I must be missing something here. And while a bit off topic, you should probably inject the UserRepository into your UserCalculator. - Cerad

1 Answers

3
votes

You can use integration test for this purpose:

This class extends KernelTestCase

class UserTest extends KernelTestCase
{
    public function testCalculateTotalUsersReturnsInteger()
    {

        self::bootKernel();
        $userCalculator = self::$kernel->getContainer()
        ->get('test.'.UserCalculator::class);


        $result = $userCalculator->calculateTotalUsers();

        $this->assertInternalType('int', $result);
    }
}

In the services_test.yml you need to register this service:

test.App\User\UserCalculator: '@App\User\UserCalculator'

And the you can call your method:

 $result = $userCalculator->calculateTotalUsers();
 $this->assertInternalType('int', $result);

and use Assert for testing.

Documentation about integration tests, I got this example from there: https://knpuniversity.com/screencast/phpunit/integration-tests

UPDATE: If you want to use UniTest to test in complete isolation and you getting error: Trying to Configure method which cannot be configured...

You need to mock your repository directly in the first mock

$userReppsitory = $this->createMock(UserRepository::class)

And yoy need to change method('find') to method('findLastRegisteredUsers')

UPDATE Using UnitTest

use PHPUnit\Framework\TestCase;
use Doctrine\Common\Persistence\ObjectManager;
use App\User\UserCalculator;
class UserTest extends TestCase
{
     public function testCalculateTotalUsersReturnsInteger()
        {

            $employee = new User();
            $employee->setFullname('test');

            // Now, mock the repository so it returns the mock of the employee
            $employeeRepository = $this->createMock(\App\Repository\UserRepository::class);

            $employeeRepository->expects($this->any())
                ->method('findLastRegisteredUsers')
                ->willReturn($employee);
            // Last, mock the EntityManager to return the mock of the repository
            $objectManager = $this->createMock(ObjectManager::class);

            $objectManager->expects($this->any())
                ->method('getRepository')
                ->willReturn($employeeRepository);

            $userCalculator = new UserCalculator($objectManager);

            $result = $userCalculator->calculateTotalUsers();
        } 
}