Я новичок в мире тестирования, и я хочу убедиться, что я на правильном пути.
Я пытаюсь настроить модульные тесты в проекте symfony2 с помощью phpunit.
PHPUnit работает, и простые тесты контроллера по умолчанию работают нормально. (Тем не менее, речь идет не о функциональном тестировании, а модульном тестировании моего приложения.)
Мой проект в значительной степени зависит от взаимодействия с базами данных, и, насколько я понимаю из документации phpunit, я должен создать класс на основе \PHPUnit_Extensions_Database_TestCase
, затем создайте приспособления для моего db и работайте оттуда.
Тем не менее, symfony2 предлагает только класс WebTestCase
, который выходит только из \PHPUnit_Framework_TestCase
из коробки.
Как я могу предположить, что я должен создать свой собственный DataBaseTestCase
, который в основном копирует WebTestCase
, только разница в том, что он простирается от \PHPUnit_Extensions_Database_TestCase
и реализует все его абстрактные методы?
Или существует ли еще один "встроенный" рекомендуемый рабочий процесс для symfony2 в отношении тестов, ориентированных на базу данных?
Как я хочу убедиться, что мои модели хранят и извлекают правильные данные, я не хочу в конечном итоге полностью проверить специфику доктрины.
TL;DR:
Единичное тестирование имеет смысл только для служб, а не для репозиториев. И эти службы могут использовать mocks менеджера сущностей.
Мой фактический прецедент для моего приложения на самом деле довольно хорошо отразился на документах symfony2 на как протестировать код, который взаимодействует с данным databse, Они предоставляют этот пример для теста обслуживания:
Класс обслуживания:
use Doctrine\Common\Persistence\ObjectManager;
class SalaryCalculator
{
private $entityManager;
public function __construct(ObjectManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function calculateTotalSalary($id)
{
$employeeRepository = $this->entityManager
->getRepository('AppBundle:Employee');
$employee = $employeeRepository->find($id);
return $employee->getSalary() + $employee->getBonus();
}
}
Класс обслуживания:
namespace Tests\AppBundle\Salary;
use AppBundle\Salary\SalaryCalculator;
use AppBundle\Entity\Employee;
use Doctrine\ORM\EntityRepository;
use Doctrine\Common\Persistence\ObjectManager;
class SalaryCalculatorTest extends \PHPUnit_Framework_TestCase
{
public function testCalculateTotalSalary()
{
// First, mock the object to be used in the test
$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));
// Now, mock the repository so it returns the mock of the employee
$employeeRepository = $this
->getMockBuilder(EntityRepository::class)
->disableOriginalConstructor()
->getMock();
$employeeRepository->expects($this->once())
->method('find')
->will($this->returnValue($employee));
// Last, mock the EntityManager to return the mock of the repository
$entityManager = $this
->getMockBuilder(ObjectManager::class)
->disableOriginalConstructor()
->getMock();
$entityManager->expects($this->once())
->method('getRepository')
->will($this->returnValue($employeeRepository));
$salaryCalculator = new SalaryCalculator($entityManager);
$this->assertEquals(2100, $salaryCalculator->calculateTotalSalary(1));
}
}
Нет тестовой базы данных, необходимой для такого теста, только (болезненным) насмешкой.
Как важно проверить бизнес-логику, а не уровень сохранения.
Только для функционального теста имеет смысл иметь собственную тестовую базу данных, которую следует строить и сносить потом, и большой вопрос должен быть:
Когда функциональный тест имеет смысл?
Раньше я думал, что проверить все, что есть правильный ответ; но после работы с большим количеством устаревшего программного обеспечения, которое само по себе было едва ли основано на тестах, я стал немного более ленивым прагматичным и рассмотрел определенную функциональность как работающую, пока не доказал обратную ошибку.
Предположим, у меня есть приложение, которое анализирует XML, создает объект из него и сохраняет эти объекты в базе данных. Если логика, которая хранит объекты в базе данных, как известно, работает (как в: компания требует данных и пока еще не сломалась), и даже если эта логика является большой уродливой кучей дерьма, нет неотложная необходимость проверить это. Поскольку все, что мне нужно, чтобы убедиться, что мой синтаксический анализатор XML извлекает правильные данные. Я могу сделать вывод, что нужные данные будут сохранены.
Существуют сценарии, где функциональный тест очень важен, т.е. если нужно писать интернет-магазин. Там было бы критически важным для бизнеса, что купленные предметы будут храниться в базе данных, и здесь функциональный тест со всей тестовой базой данных имеет абсолютный смысл.
Я никогда не использовал PHPUnit_Extensions_Database_TestCase
, главным образом потому, что по двум причинам:
Мой путь в теории...
Я использую doctrine/doctrine-fixtures-bundle для светильников (независимо от цели) и настроил всю базу данных со всеми приспособлениями. Затем я выполняю все тесты против этой базы данных и не забудьте создать базу данных, если тест изменил ее.
Преимущества в том, что мне больше не нужно настраивать базу данных, если тест читает только, но ничего не меняет. Для изменений мне нужно сбросить его и создать его снова или обязательно вернуть изменения.
Я использую sqlite для тестирования, потому что я могу настроить базу данных, а затем скопировать файл sqlite и заменить его на чистый, чтобы вернуть исходную базу данных. Таким образом, мне не нужно бросать базу данных, создавать ее и снова загружать все приборы для чистой базы данных.
... и в коде
Я написал статью о том, как я делаю тесты базы данных с symfony2 и phpunit.
Хотя он использует sqlite, я думаю, что можно легко внести изменения в использование MySQL или Postgres или что-то еще.
Мышление дальше
Вот некоторые другие идеи, которые могут сработать:
Вы можете использовать этот класс:
<?php
namespace Project\Bundle\Tests;
require_once dirname(__DIR__).'/../../../app/AppKernel.php';
use Doctrine\ORM\Tools\SchemaTool;
abstract class TestCase extends \PHPUnit_Framework_TestCase
{
/**
* @var Symfony\Component\HttpKernel\AppKernel
*/
protected $kernel;
/**
* @var Doctrine\ORM\EntityManager
*/
protected $entityManager;
/**
* @var Symfony\Component\DependencyInjection\Container
*/
protected $container;
public function setUp()
{
// Boot the AppKernel in the test environment and with the debug.
$this->kernel = new \AppKernel('test', true);
$this->kernel->boot();
// Store the container and the entity manager in test case properties
$this->container = $this->kernel->getContainer();
$this->entityManager = $this->container->get('doctrine')->getEntityManager();
// Build the schema for sqlite
$this->generateSchema();
parent::setUp();
}
public function tearDown()
{
// Shutdown the kernel.
$this->kernel->shutdown();
parent::tearDown();
}
protected function generateSchema()
{
// Get the metadatas of the application to create the schema.
$metadatas = $this->getMetadatas();
if ( ! empty($metadatas)) {
// Create SchemaTool
$tool = new SchemaTool($this->entityManager);
$tool->createSchema($metadatas);
} else {
throw new Doctrine\DBAL\Schema\SchemaException('No Metadata Classes to process.');
}
}
/**
* Overwrite this method to get specific metadatas.
*
* @return Array
*/
protected function getMetadatas()
{
return $this->entityManager->getMetadataFactory()->getAllMetadata();
}
}
И затем вы можете протестировать свою сущность. Что-то вроде этого (при условии, что у вас есть объект пользователя)
//Entity Test
class EntityTest extends TestCase {
protected $user;
public function setUp()
{
parent::setUp();
$this->user = new User();
$this->user->setUsername('username');
$this->user->setPassword('p4ssw0rd');
$this->entityManager->persist($this->user);
$this->entityManager->flush();
}
public function testUser(){
$this->assertEquals($this->user->getUserName(), "username");
...
}
}
Надеюсь на эту помощь.
Источник: theodo.fr/blog/2011/09/symfony2-unit-database-tests