Как создать тесты с объектами Doctrine, не сохраняя их (как установить id)

Я работаю над тестами для проекта Symfony2, и сейчас я ищу способ создания тестов с объектами сущностей, не сохраняя их. Проблема в том, что id является частным полем, и для этого нет настройки. Я могу создать новый объект и установить некоторые свойства, но я не могу проверить что-либо с вызовами getId().

$entity = new TheEntity();
// Can't set ID!
$entity->setProperty('propertyValue');

$name = $entity->getProperty(); // OK
$id = $entity->getId(); // not OK - null

Резолюции, о которых я знаю:

  • Инициализация всего ядра (Symfony WebTestCase:: createKernel()) и сохранение сущностей
  • Создание макета для каждой сущности, которая вернет действительный идентификатор
  • Взломается как статический метод в классе TheEntity, возвращающем инициализированный объект, или добавление setter для поля id

Каков рекомендуемый способ справиться с этим, как получить объект для тестирования чистым и быстрым способом с идентификатором набора?

Edit

Оказалось, что я могу решить это с насмешкой... извините, я все еще учусь. Я искал чистый, стандартный, но быстрый способ добиться успеха. Однако я забыл о втором параметре getMock() - то есть мне не нужно издеваться над каждым методом сущности, которую я собираюсь использовать. Я хотел избежать нескольких ->expects()->method()->will() и т.д. И это достигается добавлением: array('getId'). Этот вспомогательный метод решает проблему:

protected function getEntityMock($entityClass, $id)
{
    $entityMock = $this->getMock($entityClass, array('getId'));
    $entityMock
            ->expects($this->any())
            ->method('getId')
            ->will($this->returnValue($id));

    return $entityMock;
}

При создании многих объектов одного и того же класса все можно, конечно, упростить, используя дополнительные вспомогательные методы, такие как:

protected function getTheEntityMock($id)
{
    return $this->getEntityMock('\The\NameSpace\TheEntity', $id);
}

Единственное ограничение заключается в том, что сам объект не может использовать свойство id, только getId() getter.

Любые ценные данные по-прежнему приветствуются, но я верю, что PHPUnit getMock() решил это хорошо.

Ответ 1

Вы можете настроить doctrine для использования базы данных в памяти в app/config/config_test.yml.

# app/config/config_test
doctrine:
    dbal:
        driver: pdo_sqlite
        path: :memory:
        memory: true

Это ускоряет процесс, и вы можете быстро сохранить некоторые приборы в методе setUp(), который будет иметь (автоматически сгенерированный) идентификатор после промывки... все, не испортив вашу "настоящую" базу данных.

Вы можете найти вдохновение в этом ответе и этот пост в блоге.

Ответ 2

Другим способом является создание дочернего объекта вашей сущности (это может жить в папке с тестами, если вы хотите сохранить чистоту), а затем добавить метод setId() к дочернему

class TestableEntity extends \My\Namespace\Entity
{
    public function setId($id)
    {
       $this->id = $id;

       return $this;
    }
}

Затем ваши тесты должны проверить TestableEntity, а не реальную сущность. Пока свойство id в \My\Namespace\Entity защищено, а не частное, оно может быть установлено через TestableEntity.

Ответ 3

Это немного устарело, но стоит сказать, что лучше всего иметь вспомогательный метод, который использует Reflection для изменения эти защищенные значения.

Пример:

public function set($entity, $value, $propertyName = 'id')
{
    $class = new ReflectionClass($entity);
    $property = $class->getProperty($propertyName);
    $property->setAccessible(true);

    $property->setValue($entity, $value);
}

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