Laravel Unit Testing - add cookie to request?

Viewed 4982

I want to send a cookie with json POST:

public function testAccessCookie()
{
    $response = $this->json('POST', route('publications'))->withCookie(Cookie::create('test'));
    //some asserts
}

publications route has some middleware:

public function handle($request, Closure $next)
{
    Log::debug('cookie', [$request->cookies]);

    //cookie validation

    return $next($request);
}

But while running testAccessCookie(), there is [null] inside log. No cookies attached.

What's wrong?

There is no such problem with real (in-browser) requests.

3 Answers

You can add cookies to calls in tests:

$cookies = ['test' => 'value'];

$response = $this->call('POST', route('publications'), [], $cookies);

See https://laravel.com/api/5.4/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.html#method_call

However you will run into a cookie encryption problem. You can temporarily disable cookies during testing with:

use Illuminate\Cookie\Middleware\EncryptCookies;

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

    return $this;
}

Adding $this->disableCookiesEncryption('test'); at the start of the test.

You may need to add headers to specify a json response.

This should work in recent versions (Laravel 6):

Either:

$this->disableCookieEncryption();

or:

$cookies = ['test' => encrypt('value', false)];

$response = $this->call('POST', route('publications'), [], $cookies);

Since Laravel 5.2 you get the \App\Http\Middleware\EncryptCookies::class middleware defined by default in the web middleware group and it will set all unencrypted cookies to null.

Unfortunately all cookies you send with $request->call(), $request->get() and $request->post() in unit testing are usually unencrypted and nothing in the official documentation tells you they need to be encrypted.

If you don't want to call $request->disableCookieEncryption() everytime, as a permanent solution you can simply redefine the isDisabled() method in App\Http\Middleware\EncryptCookies.php to ignore cookies encryption during unit testing.

Here is the implementation I made for Laravel 6.x, it should work on earlier versions too.

<?php

namespace App\Http\Middleware;

use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;

class EncryptCookies extends Middleware
{
    /**
     * The names of the cookies that should not be encrypted.
     *
     * @var array
     */
    protected $except = [
        //
    ];

    public function isDisabled($name)
    {
        if (app()->runningUnitTests()) {
            return true;    // Disable cookies encryption/decryption during unit testing
        }

        return parent::isDisabled($name);
    }
}
Related