symfony test app.user在twig中为null

时间:2018-05-12 20:25:37

标签: php symfony twig

我在树枝上使用app.user。当我编写测试时,我有一个用户登录,它在控制器中工作正常。但是在twig中,当访问app.user时,它为null。

以下是我如何在测试单元中记录用户:

    $user = new User;
    $user->setRole('ROLE_ADMIN');
    $user->setUsername('admin');
    $firewallContext = 'main';
    $token = new UsernamePasswordToken(serialize($user), null, $firewallContext, array('ROLE_ADMIN'));
    $session->set('_security_'.$firewallContext, serialize($token));
    $session->save();

    $cookie = new Cookie($session->getName(), $session->getId());
    $this->client->getCookieJar()->set($cookie);

我错过了什么让它在树枝上工作?

2 个答案:

答案 0 :(得分:0)

我有一个类似的问题(正确登录)最终为我解决的是改变我登录的方式,这是我的授权客户,你可以根据你的目的调整它。 (symfony版本:2.8)

private function createAuthorizedClient() 
{
      $client = static::createClient();
      $container = static::$kernel->getContainer();
      $session = $container->get('session');
      $token = new UsernamePasswordToken('john.doe', null, 'main', array('ROLE_ADMIN'));
      $session->set('_security_main', serialize($token));
      $session->save();
      $client->getCookieJar()->set(new Cookie($session->getName(), $session->getId()));
      return $client;
}

答案 1 :(得分:-1)

我无法告诉您如何在测试环境中设置Twig来提供app.user,但是我已经做出了一种变通方法,几乎​​可以实现相同的功能。

简而言之:

  • 创建一个Twig扩展名以将当前用户吸引到Twig模板中(例如get_user())。
  • get_user():它检查它是否在APP_ENV == test中运行。如果是,则从UsernamePasswordToken实例获取User实例,否则从当前Twig Environment实例返回存储的用户。

对于Symfony 4.3+,以下代码应该是开箱即用的,但未经测试。


App / TwigExtensions / SecurityExtension.php

以下扩展名在Twig模板中提供了get_user()

<?php

namespace App\TwigExtension;

use App\Entity\User;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Twig\Environment;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

class SecurityExtension extends AbstractExtension
{
    protected $currentUser;
    protected $session;
    protected $twigEnvironment;

    public function __construct(
        Environment $twigEnvironment,
        SessionInterface $session
    ) {
        $this->session = $session;
        $this->twigEnvironment = $twigEnvironment;
    }

    public function getUser(): User
    {
        if (null !== $this->currentUser) {
            return $this->currentUser;
        }

        /*
         * if env == test
         */
        if ('test' == $_SERVER['APP_ENV']) {
            $token = \unserialize($this->session->get('_security_main'));
            if ($token instanceof UsernamePasswordToken && true === $token->isAuthenticated()) {
                $this->currentUser = $token->getUser();
            }
        } else {
            /*
             * normal way
             */
            $this->currentUser = $this->twigEnvironment->getGlobals()['app']->getToken()->getUser();
        }

        return $this->currentUser;
    }

    /**
     * @return array
     */
    public function getFunctions(): array
    {
        return [
            new TwigFunction('get_user', [$this, 'getUser']),
        ];
    }
}

测试环境

在下面的完整测试示例中,如何调用控制器动作,该动作在后面使用Twig。

<?php

use App\Entity\User;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\BrowserKit\Cookie;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;

class OrderControllerTest extends WebTestCase
{
    public function testEdit(): void
    {
        $client = static::createClient();

        $user = new User();
        // ... adapt it if you want

        $this->authenticateClientInstance($client, $user);

        // make the request
        $crawler = $client->request('GET', '/admin/order/');

        $response = $client->getResponse();

        // check the response
        $this->assertEquals(200, $response->getStatusCode());
    }

    protected function authenticateClientInstance(KernelBrowser $client, User $user): void
    {
        $session = $client->getContainer()->get('session');

        $token = new UsernamePasswordToken($user, null, 'main', ['ROLE_USER']);
        $session->set('_security_main', \serialize($token));
        $session->save();

        $cookie = new Cookie($session->getName(), $session->getId());
        $client->getCookieJar()->set($cookie);
    }
}

通过树枝访问

例如:

{% set user = get_user() %}

摘要

该解决方案被认为不是最佳方法。问题是,您需要使用许多Symfony组件,这些组件可能不适合与非Symfony组件一起使用。如果他们更改了API,您也可能会遇到问题。

我通过进一步测试确保了该方法的有效性,以确保其有效。您也应该这样做,以防Symfony 5.0+损坏。