Вопрос по symfony, php – Как настроить модульные тесты в Symfony2 с использованием PHPUnit?

43

Я новичок в мире тестирования и хочу убедиться, что я на правильном пути.

Я пытаюсь настроить юнит-тесты вsymfony2 использование проектаphpunit.

PHPUnit работает, и простые тесты контроллера по умолчанию работают нормально. (Тем не менее, речь идет не о функциональном тестировании, а о модульном тестировании моего приложения.)

Мой проект сильно зависит от взаимодействия с базой данных, и, насколько я понимаю издокументация phpunitЯ должен создать класс на основе\PHPUnit_Extensions_Database_TestCase, а затем создать светильники для моей базы данных и работать оттуда.

Еще,symfony2 только предлагаетWebTestCase класс, который распространяется только от\PHPUnit_Framework_TestCase из коробки.

Так что я прав, если предположить, что я должен создать свой собственныйDataBaseTestCase который в основном копируетWebTestCaseРазница лишь в том, что он простирается от\PHPUnit_Extensions_Database_TestCase и реализует все свои абстрактные методы?

Или есть другой "встроенный"? рекомендуемый рабочий процесс дляsymfony2 относительно тестов, ориентированных на базу данных?

Поскольку я хочу убедиться, что мои модели хранят и извлекают правильные данные, я не хочу в конечном итоге тестировать особенностиdoctrine случайно.

@JasonSwett Нет. Я только что получил награду из-за отсутствия удовлетворительного ответа. k0pernikus
Я работаю над той же проблемой. Есть удача? Jason Swett

Ваш Ответ

3   ответа
3

ТЛ; др:

  • If and only if you want to go the whole functional test route, then I recommend looking up Sgoettschkes's answer.
  • If you want to unit test your application and have to test code that interacts with the database, either read on or jump directly to symfony2 docs


В моем первоначальном вопросе были некоторые аспекты, которые проясняют, что мое понимание различий между модульным тестированием и функциональными тестами отсутствовало. (Как я уже писал, я хочу провести модульное тестирование приложения, но в то же время я также говорил о контроллере; это функциональный тест по определению).

Модульное тестирование имеет смысл только для сервисов, а не для репозиториев. И эти сервисы могут использовать макеты менеджера сущностей. (Я бы даже сказал так: если возможно, напишите службы, которые ожидают, что в них будут передаваться только сущности. Тогда вам нужно всего лишь создать макеты этих сущностей, и ваши юнит-тесты вашей бизнес-логики станут очень простыми.)

Фактический пример использования моего приложения довольно хорошо отражен в документации по symfony2 накак проверить код, который взаимодействует с базой данных.

Они предоставляют этот пример для сервисного теста:

Класс обслуживания:

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));
    }
}

Для такого рода тестов база данных не требуется, только издевательство.

Так же важно протестировать бизнес-логику, а не уровень персистентности.

Только для функциональных тестов имеет смысл иметь свою собственную тестовую базу данных, которую потом нужно создавать и разбирать, и большой вопрос должен быть:

Когда функциональный тест имеет смысл?

Раньше я думал, чтоtest all the things правильный ответ; Тем не менее, после работы с большим количеством устаревшего программного обеспечения, которое само по себе было разработано только на тестовом уровне, я стал немного болееlazyпрагматичны и рассматривают определенные функции как работающие, пока ошибка не будет доказана.

Предположим, у меня есть приложение, которое анализирует XML, создает из него объект и сохраняет эти объекты в базе данных. Если известно, что логика, которая хранит объекты в базе данных, работает (как в случае: компания требует данные и пока не сломана), и даже если эта логика представляет собой большую уродливую кучу дерьма, нетimminent нужно проверить это. Как и все, мне нужно убедиться, что мой анализатор XML извлекает правильные данные. Из своего опыта я могу сделать вывод, что правильные данные будут храниться.

Существуют сценарии, в которых функциональное тестирование очень важно, т. Е. Если бы нужно было написать онлайн-магазин. Там было бы критически важно, чтобы купленные товары были сохранены в базе данных, а здесь функциональные тесты со всей тестовой базой данных имеют абсолютный смысл.

35

Я никогда не использовалPHPUnit_Extensions_Database_TestCaseВ основном потому, что по этим двум причинам:

  • It doesn't scale well. If you set up and tear down the database for every single test and you have a application which relies heavily on the database, you end up creating and dropping the same schema over and over again.
  • I like to have my fixtures not only within my tests but also within my development database and some fixtures are even needed for production (initial admin user or product categories or whatever). Having them inside an xml which can only be used for phpunit doesn't seem right to me.

My way in theory...

Я используюдоктрина / Доктрина-светильники-расслоение для приборов (независимо от того, для чего они предназначены) и настройки всей базы данных со всеми приборами. Затем я выполняю все тесты для этой базы данных и проверяю ее заново, если тест изменил ее.

Преимущества состоят в том, что мне не нужно заново настраивать базу данных, если тест только читает, но ничего не меняет. Для внесения изменений я должен сделать сброс и создать его заново или обязательно отменить изменения.

Я использую sqlite для тестирования, потому что я могу настроить базу данных, затем скопировать файл sqlite и заменить его на чистый, чтобы вернуть исходную базу данных. Таким образом, мне не нужно удалять базу данных, создавать ее и снова загружать все приборы для чистой базы данных.

...and in code

Я написалстатья о том, как я делаю тесты базы данных с symfony2 и phpunit.

Хотя он использует sqlite, я думаю, что можно легко внести изменения, чтобы использовать MySQL или Postgres или что-то еще.

Thinking further

Вот некоторые другие идеи, которые могут работать:

  • I once read about a test setup where before you use the database you start a transaction (within the setUp method) and then use the tearDown to rollback. That way you don't need to set up the database again and just need to initialize it once.
  • My setup described above has the drawback that the database is set up every time phpunit is executed, even if you only run some unit tests with no database interaction. I am experimenting with a setup where I use a global variable which indicates if the database was set up and then within the tests call a method whcih checks this variable and initializes the database if it didn't happened yet. That way only when a tests needs the database the setup would happen.
  • One problem with sqlite is that it doesn't work the same as MySQL in some rare cases. I had an issue once where something behaved different in MySQL and sqlite causing a test to fail when in with MySQL everything worked. I cannot remember what it was exactly.
Error: User Rate Limit ExceededLiip/FunctionalTestBundleError: User Rate Limit Exceeded
Here'sError: User Rate Limit Exceeded
Error: User Rate Limit Exceededcvuorinen.net/2012/10/…
0

Вы можете использовать этот класс:

<?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

Error: User Rate Limit Exceededtheodo.fr/blog/2011/09/symfony2-unit-database-tests
Error: User Rate Limit Exceeded

Похожие вопросы