Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Twig_Environment in Service Class leads to DIC RuntimeExtension only when unittesting in Symfony 2.8

I use Twig_Environment to render html mails to be sent. I have a NotificationService Class which is used by other services to send those mails.

In a normal usage, everything is working, but since updating to 2.8 the unittest fail with: Symfony\Component\DependencyInjection\Exception\RuntimeException: You have requested a synthetic service ("kernel"). The DIC does not know how to construct this service

I debugged the StackTrace and the problem seems to be Twig_Environment (which uses the file_locator which injects kernel)

/**
 * 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);
    }
}

Any pointers on how to solve this?

EDIT: (on request)

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]
}   

EDIT2:

/**
 * 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);
    }
}

EDIT3: Geting the Kernel seems to have changed sometimes in the past. see http://symfony.com/doc/master/cookbook/testing/doctrine.html

i changed my constructor from:

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();
}

to:

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

    self::bootKernel();

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

but unfortunatly it doesnt fix the RuntimeException when tests use the Twig_Env.

like image 786
Rufinus Avatar asked Dec 19 '15 15:12

Rufinus


1 Answers

You should not boot the kernel in the test case's constructor. This way it will be shutdown after the first test has been executed (see https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php#L186-L189). You should rather boot the kernel in the test case's setUp() method.

like image 174
xabbuh Avatar answered Sep 28 '22 08:09

xabbuh