在Symfony 4中使用addReference测试夹具

时间:2019-10-25 16:12:18

标签: symfony phpunit

我正在研究Symfony的项目,并且在用phpunit测试时遇到一些问题。

我有带有addReference的StatusFixtures,可以在BriefFixtures中使用,并且当我执行doctrine:fixtures:load时可以正常工作(正确依赖于在Brief之前加载Status)。 但是,当我使用这些灯具运行测试时,会出现以下错误:Error: Call to a member function addReference() on null

我的StatusFixtures.php

<?php

namespace App\DataFixtures;

use App\Entity\Status;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;

class StatusFixtures extends Fixture
{
    const Status_Reference = 'status';

    public function load(ObjectManager $manager)
    {
        // some code to assign values

        $manager->persist($activeStatus);
        $this->addReference(self::Status_Reference, $activeStatus);
        $manager->flush();
    }
}

我的BriefFixtures.php

<?php

namespace App\DataFixtures;

use App\Entity\Brief;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\DataFixtures\DependentFixtureInterface;

class BriefFixtures extends Fixture implements DependentFixtureInterface
{
    public function load(ObjectManager $manager)
    {
        // some code to assign values
        $briefValid->setStatus($this->getReference(StatusFixtures::Status_Reference));
        $manager->persist($briefValid);
        $manager->flush();
    }

    public function getDependencies()
    {
        return array(
            StatusFixtures::class,
        );
    }
}

然后我在测试中以这种方式加载灯具

private $entityManager;

    protected function setUp()
    {
        $kernel = self::bootKernel();

        $this->entityManager = $kernel->getContainer()
            ->get('doctrine')
            ->getManager();

        $status = new StatusFixtures();
        $status->load($this->entityManager);

        $fixture = new BriefFixtures();
        $fixture->load($this->entityManager);
    }

还有我的错误Error: Call to a member function addReference() on null

来自StatusFixtures的

$this似乎为空,但是我不明白为什么当我加载夹具时此方法可以正常工作,而当我运行测试时却不能再使用。

也许setUp()中缺少某些内容?

谢谢您的帮助

1 个答案:

答案 0 :(得分:1)

问题出在Symfony documentation for Fixtures。它让你感觉像 $fixture->load($this->entityManager);只会加载灯具,但事实并非如此。使用命令doctrine:fixtures:load很简单,因为它不仅可以执行加载函数调用。

使用第三方解决方案将是最快,可能也是最好的解决方案。您可以使用以下几个库:

  1. liip/LiipTestFixturesBundle
  2. hautelook/AliceBundle(感谢michal)

您遇到的错误来自应该存储引用的ReferenceRepository对象,但它为null。谁真正建立了这个存储库,它是Doctrine\Common\DataFixtures\Executor\AbstractExecutor.,您需要的是一个Loader,它通过创建其工作所需的所有内容来加载治具。这些加载程序之一是您的命令Doctrine\Bundle\FixturesBundle\Loader\SymfonyFixturesLoader正在使用的doctrine:fixtures:load。您可以使用该加载程序,也可以编写自己的加载程序。 You can see该加载程序必须执行的操作才能为您提供预期的结果。但这还不是,您还需要Doctrine\Common\DataFixtures\Executor\ORMExecutor,因为Fixture是数据库实体,您需要对其进行持久化。 You can see that how doctrine:fixtures:load利用SymfonyFixturesLoader和ORMExecutor为您提供预期的结果。因此,您将必须为此编写自己的解决方案。我以前为自己设计了一个Loader,因为我不想使用第三方解决方案。我把它放在下面。它可能无法完全满足您的目的,但是如果您愿意,它将为您提供如何编写自己的Loader的想法。


namespace App\Tests\Extra;

use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Loader;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\DBAL\Platforms\MySqlPlatform;
use Doctrine\ORM\EntityManagerInterface;
use App\Tests\Extra\Exception\FixtureNotLoadedException;

class FixtureLoader
{
    private $entityManager;
    private $loader;
    private $registry;

    public function __construct(
        EntityManagerInterface $entityManager,
        ManagerRegistry $registry
    ) {
        $this->entityManager = $entityManager;
        $this->registry = $registry;
    }

    public function loadFixtures(array $classNames) : void
    {
        $this->loader = new Loader();

        foreach ($classNames as $className) {
            $this->loader->addFixture(new $className());
        }

        $executor = new ORMExecutor($this->entityManager, new ORMPurger());
        $executor->execute($this->loader->getFixtures());
    }

    public function getFixture(string $className) : Fixture
    {
        if ($this->loader == null) {
            throw new FixtureNotLoadedException(
                sprintf(
                    'The fixture %s must be loaded before you can access it.',
                    $className
                )
            );
        }

        return $this->loader->getFixture($className);
    }

    private function getPurger() : ORMPurger
    {
        $purger = new ORMPurger($this->entityManager);
        $purger->setPurgeMode(ORMPurger::PURGE_MODE_TRUNCATE);

        return $purger;
    }

    public function cleanDatabase() : void
    {
        $connection = $this->entityManager->getConnection();

        $mysql = ('ORM' === $this->registry->getName()
            && $connection->getDatabasePlatform() instanceof MySqlPlatform);
        if ($mysql) {
            $connection->query('SET FOREIGN_KEY_CHECKS=0');
        }
        $this->getPurger()->purge();
        if ($mysql) {
            $connection->query('SET FOREIGN_KEY_CHECKS=1');
        }
    }
}