How to Test A Doctrine Repository

How to Test A Doctrine Repository

See also

The main Testing guide describes how to use and set-up a database for your automated tests. The contents of this article show ways to test your Doctrine repositories.

Mocking a Doctrine Repository in Unit Tests

Unit testing Doctrine repositories is not recommended. Repositories are meant to be tested against a real database connection. However, in case you still need to do this, look at the following example.

Suppose the class you want to test looks like this:

  1. // src/Salary/SalaryCalculator.php
  2. namespace App\Salary;
  3. use App\Entity\Employee;
  4. use Doctrine\Persistence\ObjectManager;
  5. class SalaryCalculator
  6. {
  7. private $objectManager;
  8. public function __construct(ObjectManager $objectManager)
  9. {
  10. $this->objectManager = $objectManager;
  11. }
  12. public function calculateTotalSalary($id)
  13. {
  14. $employeeRepository = $this->objectManager
  15. ->getRepository(Employee::class);
  16. $employee = $employeeRepository->find($id);
  17. return $employee->getSalary() + $employee->getBonus();
  18. }
  19. }

Since the EntityManagerInterface gets injected into the class through the constructor, you can pass a mock object within a test:

  1. // tests/Salary/SalaryCalculatorTest.php
  2. namespace App\Tests\Salary;
  3. use App\Entity\Employee;
  4. use App\Salary\SalaryCalculator;
  5. use Doctrine\Persistence\ObjectManager;
  6. use Doctrine\Persistence\ObjectRepository;
  7. use PHPUnit\Framework\TestCase;
  8. class SalaryCalculatorTest extends TestCase
  9. {
  10. public function testCalculateTotalSalary()
  11. {
  12. $employee = new Employee();
  13. $employee->setSalary(1000);
  14. $employee->setBonus(1100);
  15. // Now, mock the repository so it returns the mock of the employee
  16. $employeeRepository = $this->createMock(ObjectRepository::class);
  17. // use getMock() on PHPUnit 5.3 or below
  18. // $employeeRepository = $this->getMock(ObjectRepository::class);
  19. $employeeRepository->expects($this->any())
  20. ->method('find')
  21. ->willReturn($employee);
  22. // Last, mock the EntityManager to return the mock of the repository
  23. // (this is not needed if the class being tested injects the
  24. // repository it uses instead of the entire object manager)
  25. $objectManager = $this->createMock(ObjectManager::class);
  26. // use getMock() on PHPUnit 5.3 or below
  27. // $objectManager = $this->getMock(ObjectManager::class);
  28. $objectManager->expects($this->any())
  29. ->method('getRepository')
  30. ->willReturn($employeeRepository);
  31. $salaryCalculator = new SalaryCalculator($objectManager);
  32. $this->assertEquals(2100, $salaryCalculator->calculateTotalSalary(1));
  33. }
  34. }

In this example, you are building the mocks from the inside out, first creating the employee which gets returned by the Repository, which itself gets returned by the EntityManager. This way, no real class is involved in testing.

Functional Testing of A Doctrine Repository

In functional tests you’ll make queries to the database using the actual Doctrine repositories, instead of mocking them. To do so, get the entity manager via the service container as follows:

  1. // tests/Repository/ProductRepositoryTest.php
  2. namespace App\Tests\Repository;
  3. use App\Entity\Product;
  4. use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
  5. class ProductRepositoryTest extends KernelTestCase
  6. {
  7. /**
  8. * @var \Doctrine\ORM\EntityManager
  9. */
  10. private $entityManager;
  11. protected function setUp()
  12. {
  13. $kernel = self::bootKernel();
  14. $this->entityManager = $kernel->getContainer()
  15. ->get('doctrine')
  16. ->getManager();
  17. }
  18. public function testSearchByName()
  19. {
  20. $product = $this->entityManager
  21. ->getRepository(Product::class)
  22. ->findOneBy(['name' => 'Priceless widget'])
  23. ;
  24. $this->assertSame(14.50, $product->getPrice());
  25. }
  26. protected function tearDown()
  27. {
  28. parent::tearDown();
  29. // doing this is recommended to avoid memory leaks
  30. $this->entityManager->close();
  31. $this->entityManager = null;
  32. }
  33. }

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.