在Symfony 2.8中进行单元测试时,将Twig_Environment用作服务类会导致DIC RuntimeExtension。

11

我使用Twig_Environment来渲染html邮件以发送。我有一个NotificationService类,其他服务使用它来发送这些邮件。

在正常使用情况下,一切都正常工作,但自从更新到2.8版本后,单元测试失败并出现以下错误信息: Symfony\Component\DependencyInjection\Exception\RuntimeException: You have requested a synthetic service ("kernel"). The DIC does not know how to construct this service

我调试了StackTrace,问题似乎出在Twig_Environment上(它使用注入kernel的file_locator)

/**
 * Notification Service Class
 *
 * @DI\Service("app.service.notification")
 */
class NotificationService extends Generic
{
    /**
     * @var \Twig_Environment
     */
    protected $twig;

    /**
     * @var \Swift_Mailer
     */
    protected $swiftMailer;

    /**
     * @var string
     */
    protected $mailTemplate = 'VendorAdminBundle:Email:system2.html.twig';

    /**
     * @param \Swift_Mailer     $swiftMailer
     * @param \Twig_Environment $twig
     *
     * @DI\InjectParams({
     *      "swiftMailer" = @DI\Inject("mailer"),
     *      "twig"   = @DI\Inject("twig")
     * })
     */
    public function __construct(\Swift_Mailer $swiftMailer, \Twig_Environment $twig)
    {
        $this->twig          = $twig;
        $this->swiftMailer   = $swiftMailer;
    }

    /**
     * Send notification mail to Manager
     * @param UserEntity $manager
     * @param array      $contacts
     */
    public function notifyManager(UserEntity $manager, array $contacts)
    {
        $subject = 'Lorem Ipsum';
        $templateFile = "AppBundle:Email:notifyManager.html.twig";
        $templateContent = $this->twig->loadTemplate($templateFile);
        $body = $templateContent->render(array(
            'user'      => $manager,
            'contacts'  => $contacts,
            'subject'   => $subject,
        ));

        $this->sendMail($body, $subject, $manager);
    }
}

你有解决这个问题的任何指针吗?

编辑:(按要求)

class NotificationTest extends DoctrineTestCase
{
    /**
     * @var \App\Service\Notification
     */
    protected $service;

    public function setUp()
    {
        $this->markTestSkipped('Problem with twig env');

        $this->loadFixturesFromDirectory(__DIR__ . '/DataFixtures');
        $this->loginUser('admin', $this->getUser(1));
        $this->service = $this->container->get('neos.service.notification'); // <-- exception thrown here
    }

    [test methods]
}   

编辑2:

/**
 * Class DoctrineTestCase.
 *
 * This is the base class to load doctrine fixtures using the symfony configuration
 */
class DoctrineTestCase extends TestCase
{
    /**
     * @var \Symfony\Component\DependencyInjection\Container
     */
    protected $container;

    /**
     * @var \Doctrine\ORM\EntityManager
     */
    protected $em;

    /**
     * @var string
     */
    protected $environment = 'test';

    /**
     * @var bool
     */
    protected $debug = true;

    /**
     * @var string
     */
    protected $entityManagerServiceId = 'doctrine.orm.entity_manager';

    /**
     * Constructor.
     *
     * @param string|null $name     Test name
     * @param array       $data     Test data
     * @param string      $dataName Data name
     */
    public function __construct($name = null, array $data = array(), $dataName = '')
    {
        parent::__construct($name, $data, $dataName);

        if (!static::$kernel) {
            static::$kernel = self::createKernel(array(
                'environment' => $this->environment,
                'debug' => $this->debug,
            ));
            static::$kernel->boot();
            static::$kernel->getContainer()->set('kernel', static::$kernel); //<--- Added - but doesnt help
        }

        $this->container = static::$kernel->getContainer();
        $this->em = $this->getEntityManager();
    }

    /**
     * Executes fixtures.
     *
     * @param \Doctrine\Common\DataFixtures\Loader $loader
     */
    protected function executeFixtures(Loader $loader)
    {
        $purger = new ORMPurger();
        $executor = new ORMExecutor($this->em, $purger);
        $executor->execute($loader->getFixtures());
    }

    /**
     * Load and execute fixtures from a directory.
     *
     * @param string $directory
     */
    protected function loadFixturesFromDirectory($directory)
    {
        $loader = new ContainerAwareLoader($this->container);
        $loader->loadFromDirectory($directory);
        $this->executeFixtures($loader);
    }

    /**
     * Returns the doctrine orm entity manager.
     *
     * @return object
     */
    protected function getEntityManager()
    {
        return $this->container->get($this->entityManagerServiceId);
    }
}

编辑3: 在过去的某些时候,获取内核似乎已发生变化。 请参见:http://symfony.com/doc/master/cookbook/testing/doctrine.html

我将我的构造函数从以下内容更改为:

public function __construct($name = null, array $data = array(), $dataName = '')
{
    parent::__construct($name, $data, $dataName);

    if (!static::$kernel) {
        static::$kernel = self::createKernel(array(
            'environment' => $this->environment,
            'debug' => $this->debug,
        ));
        static::$kernel->boot();
        static::$kernel->getContainer()->set('kernel', static::$kernel); //<--- Added - but doesnt help
    }

    $this->container = static::$kernel->getContainer();
    $this->em = $this->getEntityManager();
}

致:

public function __construct($name = null, array $data = [], $dataName = '')
{
    parent::__construct($name, $data, $dataName);

    self::bootKernel();

    $this->container = static::$kernel->getContainer();
    $this->em = $this->getEntityManager();
}

但不幸的是,它无法解决测试使用Twig_Env时出现的运行时异常问题。


1
也许这个页面会有所帮助。 - Radu Murzea
但是AppKernel不应该设置内核实例吗?单元测试通过相同的引导类进行。 - Rufinus
是的,但这并没有帮助,一旦我从容器中请求类,错误就会被抛出。请参见上文。 - Rufinus
1
你能否也展示一下 DoctrineTestCase 类?我有一种感觉,设置内核或获取容器时可能会出现问题。 - Dan Mironis
2
在测试用例的构造函数中不应启动内核。这样做将在执行第一个测试后关闭它(请参见https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php#L186-L189)。相反,您应该在测试用例的“setUp()”方法中启动内核。 - xabbuh
显示剩余14条评论
1个回答

9

请根据您的评论编辑您的答案(您在答案中有一个无效的链接) - Sergio Ivanuzzo
谢谢xabbuh。正是我们的问题。只需在setUp()方法中而不是在setUpBeforeClass()中启动内核,就可以解决这个问题。 - Wulf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接