Как тестировать хранилище Doctrine

Дата обновления перевода 2024-07-29

Как тестировать хранилище Doctrine

See also

В главном руководстве по тестированию описано, как использовать и настраивать базу данных для ваших автоматизированных тестов. Содержание данной статьи показывает способы тестирования хранилищ Doctrine.

Имитация хранилища Doctrine в модульных тестах

Модульное тестирование хранилищ Doctrine не рекомендуется. Хранилища предназначены для тестирования на реальном соединении с базой данных. Однако, если вам все же необходимо это сделать, рассмотрите следующий пример.

// src/Salary/SalaryCalculator.php namespace AppSalary;

use AppEntityEmployee; use DoctrineORMEntityManager;

class SalaryCalculator { public function __construct( private EntityManager $entityManager, ) { }

public function calculateTotalSalary(int $id): int { $employeeRepository = $this->entityManager ->getRepository(Employee::class); $employee = $employeeRepository->find($id);

return $employee->getSalary() + $employee->getBonus();

}

}

Так как EntityManagerInterface внедряется в класс через конструктор, то очень легко передать объект-макет в пределах теста:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// tests/Salary/SalaryCalculatorTest.php
namespace App\Tests\Salary;

use App\Entity\Employee;
use App\Salary\SalaryCalculator;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityRepository;
use PHPUnit\Framework\TestCase;

class SalaryCalculatorTest extends TestCase
{
    public function testCalculateTotalSalary(): void
    {
        $employee = new Employee();
        $employee->setSalary(1000);
        $employee->setBonus(1100);

        // Теперь, сымитируйте хранилише, чтобы оно возвращало имитацию работника
        $employeeRepository = $this->createMock(EntityRepository::class);
        $employeeRepository->expects($this->any())
            ->method('find')
            ->willReturn($employee);

        // Наконец, сымитируйте EntityManager, чтобы возвращать имитацию хранилища
        // (это не нужно, если тестируемый класс внедрряет хранилище, которое он
        // использует, вместо всего менеджера объектов)
        $entityManager = $this->createMock(EntityManager::class);
        $entityManager->expects($this->any())
            ->method('getRepository')
            ->willReturn($employeeRepository);

        $salaryCalculator = new SalaryCalculator($entityManager);
        $this->assertEquals(2100, $salaryCalculator->calculateTotalSalary(1));
    }
}

В этом примере, вы строите макеты наизнанку, вначале создавая служащего, который возвращается Repository, который, в свою очередь, возвращается EntityManager. Таким образом, в тестировании не участвует ни один настоящий класс.

Функциональное тестирование хранилища Doctrine

В функциональных тестах вы будете делать запросы к базе данных, используя реальные хранилища Doctrine, вместо того, чтобы имитировать их. Для этого получите менеджер сущностей через сервис-контейнер следующим образом:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// tests/Repository/ProductRepositoryTest.php
namespace App\Tests\Repository;

use App\Entity\Product;
use Doctrine\ORM\EntityManager;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

class ProductRepositoryTest extends KernelTestCase
{
    private ?EntityManager $entityManager;

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

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

    public function testSearchByName(): void
    {
        $product = $this->entityManager
            ->getRepository(Product::class)
            ->findOneBy(['name' => 'Priceless widget'])
        ;

        $this->assertSame(14.50, $product->getPrice());
    }

    protected function tearDown(): void
    {
        parent::tearDown();

        // это рекомендуется делать, чтобы избежать утечек памяти
        $this->entityManager->close();
        $this->entityManager = null;
    }
}