How to create tests w/Doctrine entities without pe

2020-05-26 09:50发布

I'm working on tests for a Symfony2 project, and right now I'm looking for a way to create tests involving entity objects without persisting them. The problem is: id is a private field and there is no setter for that. I can create new object and set some properties, but I can't test anything involving getId() calls.

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

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

The resolutions I'm aware of:

  1. Initializing whole kernel (Symfony's WebTestCase::createKernel()) and persisting the entities
  2. Creating a mock object for every entity, that will return valid ID
  3. Hacks like static method in TheEntity class returning initialized object, or adding setter for id field

What is the recommended way to deal with this, how to obtain an entity for testing in clean and fast way, with set ID?

Edit

Turned out I can solve this with mocking... sorry, I'm still learning. I was looking for a clean, standard, yet fast way to get things done. However I forgot about second parameter of getMock() - that is I don't have to mock every method of the entity I am going to use. I wanted to avoid multiple ->expects()->method()->will() etc. And this is achieved by adding: array('getId'). This helper method solves the problem:

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

    return $entityMock;
}

When creating many entities of the same class, things can be of course simplified more by more helper methods like this one:

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

The only limitation is that the entity itself cannot use id property, only getId() getter.

Any valuable input is still welcome, but I belive PHPUnit's getMock() solved this well.

3条回答
我只想做你的唯一
2楼-- · 2020-05-26 10:20

Another way is to create a child of your entity (this could live in the tests folder if you want to keep things clean), then add the "setId()" method to the child

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

       return $this;
    }
}

Your tests should then test the TestableEntity, rather than the real entity. As long as the "id" property in \My\Namespace\Entity is protected, rather than private, it can be set via the TestableEntity.

查看更多
做个烂人
3楼-- · 2020-05-26 10:27

You can configure doctrine to use an in-memory database in your app/config/config_test.yml.

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

This speeds up the process and you can quickly persist some fixtures in the setUp() method that will have (auto-generated) id's after flushing... all without messing with your "real" database.

You can find some inspiration in this answer and this blog post.

查看更多
走好不送
4楼-- · 2020-05-26 10:42

It's little bit old but it's worthy to say that maybe the best way is to have helper method that is using Reflection to alter those protected values.

Example :

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

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

put that in base test class that you extends or have it in trait and just use it when you need something like that in test. In that way you will be able to write tests without having to create dirty changes.

查看更多
登录 后发表回答