Тестирование cookies в Laravel и phpunit

27 января Asvae

Предполагается, что вы прочитали раздел документации по тестированию, а то и писали тесты самостоятельно.

Бегло пробежавшись глазами по мануалу, может показаться, что кукезы тестировать элементарно. Но давайте попробуем решить задачу на конкретном примере.

Условие

Пользователь вбивает в браузере http://site.com/register/123 и переходит на страницу. Его тут же редиректит на /register, выдавая параллельно кукез referral: 123. Пользователь регистрируется — реферал должен закрепиться в базе. Задача состоит в том, чтобы протестировать юзкеис.

Решение 1

Казалось бы, все просто:

$this->visit('register/123')->seeCookie('referral');

Вот только тест красный. Придется покопаться в коде:

Заглянув в недра visit(), наблюдаем, что она вызывает $this->followRedirects(), и запрос следует редиректам. Ларавель же, принципиально не передает кукезы при редиректе. Почему бы не разбить тест на два.

Решение 2

// Тест 1
$this->get('register/123');
$this->seeCookie('referral');

C первым тестом все хорошо. Он зеленый, как огурчик. Дальше мы его трогать не будем.

// Тест 2
$this->call('post', 'register', $this->userData, ['referral' => '123']);
$this-assertTrue($this->checkUserHasReferral('123')); // Проверить, сохранилось ли значение в базе.

А вот второй — снова неумолимо краснеет. И мало того, dd($request->cookies) в контроллере сообщает, что никаких кукезов не было и вообще вы адресом ошиблись.

Проблема здесь в том, что ларавель куки еще и шифрует. Попытаемся побороть этот вопрос.

Решение 3

$this->app->resolving(App\Http\Middleware\EncryptCookies::class,
    function ($object) {
        $object->disableFor('referral');
    });
$this->call('post', 'register', $this->userData, ['referral' => '123']);
$this-assertTrue($this->checkUserHasReferral('123'));

Что здесь происходит? В тот момент, когда ларавель попытается вызвать инстант класса EnctyptCookies, отвечающего за шифрование кукезов, триггерится вышеописанное замыкание, которое модифицирует объект так, чтобы он игнорировал куки с определенным значением. В результате, запрос обретает силу видеть нешифрованную куку.

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

/**
 * @param array|string $cookies
 * @return $this
 */
protected function disableCookiesEncryption($cookies)
{
    $this->app->resolving(EncryptCookies::class,
        function ($object) use ($cookies) {
            $object->disableFor($cookies);
        });

    return $this;
}

А в конкретном тест-кейсе, уже запустить в методе setUp():

public function setUp()
{
    parent::setUp();
    $this->disableCookiesEncryption(Referral::COOKIE_NAME);
}

Альтернативным вариантом будет, наоборот, шифровать куки перед тем, как положить в запрос:

$cookie = [ 'referral' => \Crypt::encrypt('123') ];

Заключение

И вот, тесты позеленели, а вы научились автоматически проверять кукезы без лишней головной боли.