Как имитировать HTTP-аутентификацию в функциональном тестировании

Запросы аутентификации в функциональных тестах могут замедлить весь набор тестов. Это может стать проблемой, особенно, когда тесты воспроизводят те же шаги, которым следуют пользователи для аутентификации, такие как отправку формы имени пользователя или использоваие сервисов аутентификации OAuth.

Эта статья объясняет две наиболее популярные техники для того, чтобы избежать таких проблем и создавать быстрые тести при использовании аутентификации.

Использование ускоренного механизма аутентификации только для тестирования

Если ваше приложение использует аутентификацию form_login, вы можете сделать ваши тесты более быстрыми, позволив им использовать HTTP-аутентификацию. Таким образом, ваши тесты будут аутентифицированы с помощью простого и быстрого HTTP-метода, в то время как ваши настоящие пользователи будут выполнять вход через нормальную форму входа.

Фокус в том, чтобы использовать аутентификацию http_basic в брандмауэре вашего приложения, но только в файле конфигурации, используемого тестами:

  • YAML
    1
    2
    3
    4
    5
    # app/config/config_test.yml
    security:
        firewalls:
            your_firewall_name:
                http_basic: ~
    
  • XML
    1
    2
    3
    4
    5
    6
    <!-- app/config/config_test.xml -->
    <security:config>
        <security:firewall name="your_firewall_name">
          <security:http-basic />
       </security:firewall>
    </security:config>
    
  • PHP
    1
    2
    3
    4
    5
    6
    7
    8
    // app/config/config_test.php
    $container->loadFromExtension('security', array(
        'firewalls' => array(
            'your_firewall_name' => array(
                'http_basic' => array(),
            ),
        ),
    ));
    

Теперь тесты могут аутентифицироваться через HTTP, огибая имя пользователя и пароль как переменные, используя второй аргумент createClient():

1
2
3
4
$client = static::createClient(array(), array(
    'PHP_AUTH_USER' => 'username',
    'PHP_AUTH_PW'   => 'pa$$word',
));

Имя пользователя и пароль также могут быть переданы на по-запросной основе:

1
2
3
4
$client->request('DELETE', '/post/12', array(), array(), array(
    'PHP_AUTH_USER' => 'username',
    'PHP_AUTH_PW'   => 'pa$$word',
));

Создание метки аутентификации

Если ваше приложение использует более продвинутый механизм аутентификации, вы не сможете использовать предыдущий способ, но вы все еще можете ускорить ваши тесты. Теперь фокус в том, что для обхода процесса аутентификации, вы самостоятельно создаёте метку аутентификации и сохраняете ее в сессии.

Эта техника требует некоторых знаний внутренних компонентов безопасности, но следующий пример показывает полностью весь процесс, который вы можете адаптировать под ваши потребности:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// tests/AppBundle/Controller/DefaultControllerTest.php
namespace Tests\AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\BrowserKit\Cookie;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;

class DefaultControllerTest extends WebTestCase
{
    private $client = null;

    public function setUp()
    {
        $this->client = static::createClient();
    }

    public function testSecuredHello()
    {
        $this->logIn();
        $crawler = $this->client->request('GET', '/admin');

        $this->assertSame(Response::HTTP_OK, $this->client->getResponse()->getStatusCode());
        $this->assertSame('Admin Dashboard', $crawler->filter('h1')->text());
    }

    private function logIn()
    {
        $session = $this->client->getContainer()->get('session');

        // по умолчанию контекст брандмауэра является именем брандмауэра
        $firewallContext = 'secured_area';

        $token = new UsernamePasswordToken('admin', 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);
    }
}

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.