Как тестировать код, взаимодействующий с базой данных
Как тестировать код, взаимодействующий с базой данных
Если ваш код взаимодействует с базой данных, например, считывает данные из нее
или сохраняет данные в нее, вам нужно настроить ваши тесты так, чтобы они это
учитывали. Существует множество способов справиться с этим. В модульном тестировании
вы можете создать макет Repository
и использовать ее для возвращения
ожидаемых объектов. В функциональном тесте вам может понадобиться подготовить тестовую
DB с предопределёнными значениями, чтобы убедиться, что ваш тест всегда имеет одни
и те же данные для работы.
Note
Если вы хотите тестировать ваши запросы напрямую, см. Как тестировать хранилища Doctrine.
Макетирование Repository
в модульном тесте
Если вы хотите протестировать код, который зависит от хранилища Doctrine в
изоляции, вам понадобится макетировать Repository
. Обычно вы внедряете
EntityManager
в ваш класс и используете его для получения хранилища. Это
немного усложняет вещи, так как вам понадобится делать макеты как EntityManager
,
так и вашего класса хранилища.
Tip
Существует возможность (и это хорошая идея) внедрить ваше хранилище напрямую, путём регистрации его в качестве сервиса предприятия. Это требует больших усилий по установке, но облегчает тестирование, так как вам понадобится имитировать только хранилище.
Представьте, что класс, который вы хотите протестировать, выглядит так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
// src/AppBundle/Salary/SalaryCalculator.php
namespace AppBundle\Salary;
use Doctrine\ORM\EntityManagerInterface;
class SalaryCalculator
{
private $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
public function calculateTotalSalary($id)
{
$employeeRepository = $this->entityManager
->getRepository('AppBundle:Employee');
$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 40 41 42 43 44 45 46
// tests/AppBundle/Salary/SalaryCalculatorTest.php
namespace Tests\AppBundle\Salary;
use AppBundle\Entity\Employee;
use AppBundle\Salary\SalaryCalculator;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
class SalaryCalculatorTest extends TestCase
{
public function testCalculateTotalSalary()
{
// Вначале, макетируйте объект, который будет использован в тесте
$employee = $this->createMock(Employee::class);
// используйте getMock() на PHPUnit 5.3 или ниже
// $employee = $this->getMock(Employee::class);
$employee->expects($this->once())
->method('getSalary')
->will($this->returnValue(1000));
$employee->expects($this->once())
->method('getBonus')
->will($this->returnValue(1100));
// Теперь, создайте макет хранилища так, чтобы он возвращал макет служащего
$employeeRepository = $this
->getMockBuilder(EntityManagerInterface::class)
->disableOriginalConstructor()
->getMock();
$employeeRepository->expects($this->once())
->method('find')
->will($this->returnValue($employee));
// В конце, создайте макет EntityManager так, чтобы он возвращал макет хранилища
$entityManager = $this
->getMockBuilder(EntityManagerInterface::class)
->disableOriginalConstructor()
->getMock();
$entityManager->expects($this->once())
->method('getRepository')
->will($this->returnValue($employeeRepository));
$salaryCalculator = new SalaryCalculator($entityManager);
$this->assertEquals(2100, $salaryCalculator->calculateTotalSalary(1));
}
}
В этом примере, вы строите макеты наизнанку, вначале создавая служащего, который
возвращается Repository
, который, в свою очередь, возвращается EntityManager
.
Таким образом, в тестировании не участвует ни один настоящий класс.
Изменение настроек DB для функциональных тестов
Если у вас есть функциональные тесты, вам нужно, чтобы они взаимодействовали с настоящей базой данных. В большинстве случаев, вам нужно будет использовать выделенную связь с базой данных, чтобы убедиться в том, что вы не переопределяете данные, которые вы ввели при разработке приложения, и в том, что вы сможете очищать DB перед каждым тестом.
Чтобы сделать это, вы можете указать конфигурацию DB, которая переопределяет конфигурацию по умолчанию:
- YAML
- XML
- PHP
1 2 3 4 5 6 7 8
# app/config/config_test.yml
doctrine:
# ...
dbal:
host: localhost
dbname: testdb
user: testdb
password: testdb
Убедитесь в том, что ваша база данных работает на локальном хосте и имеет установленную определённую DB и учётные данные пользователя.