Symfony 3.4功能测试

问题描述

我在使用symfony 3.4进行功能测试时遇到了一段时间的错误

我的应用程序运行自定义的GuardAuthenticator,以在CAS身份验证中对我的用户进行身份验证。这仅是为了解释上下文。在我的测试中,我不想使用它,我想使用特定的身份验证系统。

我写了功能测试。我从以下环境开始了我的测试环境:

# app/config/config_test.yml
imports:
    - { resource: config_dev.yml }

framework:
    test: ~
    session:
        storage_id: session.storage.mock_file
        name: MOCKSESSION
    profiler:
        collect: false

根据Symfony官方文档,我扩展了symfony WebTestCase来为我的测试How to Simulate HTTP Authentication in a Functional Test

设置特定的身份验证令牌。
namespace Test\AppBunble\WebTestCase;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase as SfTestCase;

class WebTestCase extends SfTestCase
{
    /**
     *
     * {@inheritDoc}
     * @see \PHPUnit_Framework_TestCase::setUp()
     */
    public function setUp()
    {
        $this->client = static::createClient();
        $this->container = $this->client->getContainer();
        $this->entityManager = $this->container->get('doctrine.orm.entity_manager');

        parent::setUp();
    }

    public function logInAs($username)
    {
        $user = $this->entityManager->getRepository(User::class)->loadUserByUsername($username);
        
        $session = $this->client->getContainer()->get('session');
        $token = new PostAuthenticationGuardToken($user,'main',$user->getRoles());
        $session->set('_security_main',serialize($token));
        
        $session->save();
        
        $cookie = new Cookie($session->getName(),$session->getId());
        $this->client->getCookieJar()->set($cookie);
    }
}

最后,我写了我的(简单)测试:

namespace Tests\AppBundle\Controller;

use Tests\AppBundle\WebTestCase;

class DefaultControllerTest extends WebTestCase
{
    public function testIndex()
    {
        $this->logInAs('admin');
        $crawler = $this->client->request('GET','/');
        $this->assertTrue($client->getResponse()->isSuccessful(),'response status is 2xx');
    }
}

我运行了PHPunit:

$ ./vendor/bin/simple-phpunit 
PHPUnit 5.7.27 by Sebastian Bergmann and contributors.

Testing app Test Suite
<br />
<b>Error</b>: <font color="FF0000"><b>Internal script failure</b><br />

为了调试,我修改了测试:

    public function testIndex()
    {
        $this->logInAs('admin');
        //$crawler = $this->client->request('GET','/');
        //$this->assertTrue($client->getResponse()->isSuccessful(),'response status is 2xx');
    }

我运行了PHPunit:

 ./vendor/bin/simple-phpunit 
PHPUnit 5.7.27 by Sebastian Bergmann and contributors.

Testing app Test Suite
.                                                                   1 / 1 (100%)

Time: 797 ms,Memory: 27.75MB

OK (1 test,0 assertions)

经过大量测试后,我发现我打电话给测试:

$this->client->request('GET','/')

它崩溃了。

更新:另一项测试。

如果我运行了此话:

    public function testIndex()
    {
        $this->logInAs('admin');
        $token = $this->client->getContainer()->get('security.token_storage');
        var_dump($token->getToken());

        //$crawler = $this->client->request('GET','response status is 2xx');
    }

它退还给我NULL ...没有令牌。

更新2

我深入到代码中,回到了symfony停止的地方。在dodispatch类的Symfony\Component\Eventdispatcher\Eventdispatcher方法中。

它在`kernel.event'事件上触发监听器。当它触发名为“ Symfony \ Bundle \ SecurityBundle \ Debug \ TraceableFirewallListener” =>“内部脚本故障”的侦听器时。

更新3 如果我比较dev.log(代表我在浏览器中运行应用程序时的日志),则该日志位于请求中:

[2020-10-23 13:55:56] request.INFO: Matched route "app_homepage". {"route":"app_homepage","route_parameters":{"_controller":"AppBundle\\Controller\\DefaultController::indexAction","_route":"app_homepage"},"request_uri":"http://localhost:8180/","method":"GET"} []
[2020-10-23 13:55:56] security.DEBUG: Read existing security token from the session. {"key":"_security_main","token_class":"Symfony\\Component\\Security\\Guard\\Token\\PostAuthenticationGuardToken"} []
[2020-10-23 13:55:56] doctrine.DEBUG: SELECT [...]
[2020-10-23 13:55:56] security.DEBUG: User was reloaded from a user provider.  [...]

但是当我运行$this->client->request('GET','/')时,我的test.log显示为:

[2020-10-23 13:55:56] request.INFO: Matched route "app_homepage". {"route":"app_homepage","request_uri":"http://localhost","method":"GET"} []

没有来自安全的踪迹。

更新4:另一项测试

我进行了此测试:

    public function testIndex()
    {
        $crawler = $this->client->request('GET','/stackTest');
        $this->assertTrue($this->client->getResponse()->isSuccessful(),'response status is 2xx');
    }

这是在security.yml中具有此配置的页面

firewalls:
    stack:
        pattern: ^/stackTest
        security: false

...然后测试成功!

 ./vendor/bin/simple-phpunit 
PHPUnit 5.7.27 by Sebastian Bergmann and contributors.

Testing app Test Suite
.                                                                   1 / 1 (100%)

Time: 1.08 seconds,Memory: 36.50MB

OK (1 test,1 assertion)

解决方法

我找到了!

如上所述,我的应用程序的身份验证是通过CAS完成的。为了进行测试,我想模拟这种身份验证。因此,我创建了一个FakeGuardAuthenticator,而不是询问CAS服务器,而是对其进行仿真并返回凭据。

因此,在我的config_test.yml配置文件中,我必须通过传递别名来覆盖guardAuthenticator。

给予:

# app/config/config_test.yml
imports:
    - { resource: config_dev.yml }
    - { resource: services_test.yml }
# app/config/service_test.yml
services:
    _defaults:
        autowire: true
        autoconfigure: true
        public: false

    app_cas.guard_authenticator:
        class : Tests\AppBundle\FakeGuardAuthenticator
        arguments:
          # [...]
        provider: cas

感谢@vstelmakh和@Vyctorya花时间回复我。