Як тестувати сховище Doctrine

Дата оновлення перекладу 2022-11-24

Як тестувати сховище Doctrine

See also

Головне керівництво з Тестування описує, як використовувати та встановлювати базу даних для ваших автоматизованих тестів. Зміст цієї статті демонструє шляхи тестування ваших сховищ Doctrine.

Імітація сховища 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
// src/Salary/SalaryCalculator.php
namespace App\Salary;

use App\Entity\Employee;
use Doctrine\Persistence\ObjectManager;

class SalaryCalculator
{
    private $objectManager;

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

    public function calculateTotalSalary($id)
    {
        $employeeRepository = $this->objectManager
            ->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
36
37
38
39
// tests/Salary/SalaryCalculatorTest.php
namespace App\Tests\Salary;

use App\Entity\Employee;
use App\Salary\SalaryCalculator;
use Doctrine\Persistence\ObjectManager;
use Doctrine\Persistence\ObjectRepository;
use PHPUnit\Framework\TestCase;

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

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

        // Нарешті, зімітуйте EntityManager, щоб повернути імітацію сховища
        // (це не потрібно, якщо тестований клас впроваджує сховище, яке
        // він використовує, замість всього менеджеру обʼєктів)
        $objectManager = $this->createMock(ObjectManager::class);
        // використайте getMock() в PHPUnit 5.3 або нижче
        // $objectManager = $this->getMock(ObjectManager::class);
        $objectManager->expects($this->any())
            ->method('getRepository')
            ->willReturn($employeeRepository);

        $salaryCalculator = new SalaryCalculator($objectManager);
        $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
40
41
// tests/Repository/ProductRepositoryTest.php
namespace App\Tests\Repository;

use App\Entity\Product;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

class ProductRepositoryTest extends KernelTestCase
{
    /**
     * @var \Doctrine\ORM\EntityManager
     */
    private $entityManager;

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

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

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

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

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

        // doing this is recommended to avoid memory leaks
        $this->entityManager->close();
        $this->entityManager = null;
    }
}