【问题标题】:How to create tests w/Doctrine entities without persisting them (how to set id)如何使用 Doctrine 实体创建测试而不持久化它们(如何设置 id)
【发布时间】:2022-04-26 23:18:46
【问题描述】:

我正在为 Symfony2 项目进行测试,现在我正在寻找一种方法来创建涉及实体对象的测试,而无需持久化它们。问题是: id 是一个私有字段,没有设置器。我可以创建新对象并设置一些属性,但我无法测试涉及 getId() 调用的任何内容。

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

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

我知道的决议:

  1. 初始化整个内核(Symfony 的 WebTestCase::createKernel())并保持实体
  2. 为每个实体创建一个模拟对象,该对象将返回有效 ID
  3. TheEntity 类中的静态方法返回初始化对象,或为 id 字段添加设置器

解决这个问题的推荐方法是什么,如何以干净快速的方式获得一个实体进行测试,并带有集合 ID?

编辑

原来我可以通过嘲笑来解决这个问题……抱歉,我还在学习。我一直在寻找一种干净、标准、快速的方式来完成工作。但是我忘记了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() 很好地解决了这个问题。

【问题讨论】:

    标签: symfony testing doctrine-orm mocking phpunit


    【解决方案1】:

    它有点老了,但值得一提的是,也许最好的方法是使用使用 Reflection 的辅助方法来更改那些受保护的值。

    例子:

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

    把它放在你扩展的基础测试类中,或者把它放在trait 中,当你在测试中需要类似的东西时使用它。通过这种方式,您将能够编写测试,而无需创建脏更改。

    【讨论】:

      【解决方案2】:

      您可以在 app/config/config_test.yml 中配置学说以使用内存数据库。

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

      这加快了进程,您可以在setUp() 方法中快速保留一些固定装置,这些固定装置在刷新后将具有(自动生成的)ID...所有这些都不会弄乱您的“真实”数据库。

      您可以在this answerthis blog post 中找到一些灵感。

      【讨论】:

      • 用于测试的单独数据库是一种保持清洁的方法,我知道我迟早会使用它。但我一直在寻找非常便宜的方法。但是,感谢您发布此示例,我不知道有这么简单的方法(没有 tmpfs)将数据库保存在内存中。
      【解决方案3】:

      另一种方法是创建实体的子实体(如果您想保持干净,它可以位于测试文件夹中),然后将“setId()”方法添加到子实体

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

      然后您的测试应该测试 TestableEntity,而不是真实的实体。 只要 \My\Namespace\Entity 中的“id”属性是受保护的,而不是私有的,就可以通过 TestableEntity 进行设置。

      【讨论】:

        【解决方案4】:

        另一种选择是使用匿名类:

        $entity = new class extends \NameSpace\TheEntity {
            public function getId() {
                return 123;
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2012-01-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-04-13
          • 1970-01-01
          • 1970-01-01
          • 2014-07-05
          相关资源
          最近更新 更多