Как тестировать код, взаимодействующий с базой данных¶
Если ваш код взаимодействует с базой данных, например, считывает данные из нее
или сохраняет данные в нее, вам нужно настроить ваши тесты так, чтобы они это
учитывали. Существует множество способов справиться с этим. В модульном тестировании
вы можете создать макет Repository
и использовать ее для возвращения
ожидаемых объектов. В функциональном тесте вам может понадобиться подготовить тестовую
DB с предопределёнными значениями, чтобы убедиться, что ваш тест всегда имеет одни
и те же данные для работы.
Note
Если вы хотите тестировать ваши запросы напрямую, см. /testing/doctrine.
Макетирование Repository
в модульном тесте¶
Если вы хотите протестировать код, который зависит от хранилища Doctrine в
изоляции, вам понадобится макетировать Repository
. Обычно вы внедряете
EntityManager
в ваш класс и используете его для получения хранилища. Это
немного усложняет вещи, так как вам понадобится делать макеты как EntityManager
,
так и вашего класса хранилища.
Tip
Существует возможность (и это хорошая идея) внедрить ваше хранилище напрямую, путём регистрации его в качестве сервиса предприятия. Это требует больших усилий по установке, но облегчает тестирование, так как вам понадобится имитировать только хранилище.
Представьте, что класс, который вы хотите протестировать, выглядит так:
// 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
внедряется в класс через конструктор, то
очень легко передать объект-макет в пределах теста:
// 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
1 2 3 4 5 6 7 8
# app/config/config_test.yml doctrine: # ... dbal: host: localhost dbname: testdb user: testdb password: testdb
- XML
1 2 3 4 5 6 7 8 9
<!-- app/config/config_test.xml --> <doctrine:config> <doctrine:dbal host="localhost" dbname="testdb" user="testdb" password="testdb" /> </doctrine:config>
- PHP
1 2 3 4 5 6 7 8 9
// app/config/config_test.php $container->loadFromExtension('doctrine', array( 'dbal' => array( 'host' => 'localhost', 'dbname' => 'testdb', 'user' => 'testdb', 'password' => 'testdb', ), ));
Убедитесь в том, что ваша база данных работает на локальном хосте и имеет установленную определённую DB и учётные данные пользователя.
Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.