Is it possible to mock document.cookie in JavaScript?

Viewed 32573

document.cookie is like a string, but it is not a string. To quote the example from the Mozilla doc:

document.cookie = "name=oeschger";
document.cookie = "favorite_food=tripe";
alert(document.cookie);
// displays: name=oeschger;favorite_food=tripe

If you tried to make a mock cookie using only a string, you would not get the same results:

var mockCookie = "";
mockCookie = "name=oeschger";
mockCookie = "favorite_food=tripe";
alert(mockCookie);
// displays: favorite_food=tripe

So, if you wanted to unit test a module that operates on the cookie, and if you wanted to use a mock cookie for those tests, could you? How?

7 Answers

I figured out that jasmine has spyOnProperty which can be used for when you want to spy on getter and setters of objects. So I solved my issue with this:

const cookie: string = 'my-cookie=cookievalue;';    
spyOnProperty(document, 'cookie', 'get').and.returnValue(cookie);

I know this is an old topic, but in my case expiring cookies was necessary so here's a solution that combines the above answers and a setTimeout call to expire cookies after X seconds:

const fakeCookies = {
    // cookie jar
    all: {},

    // timeouts
    timeout: {},

    // get a cookie
    get: function(name)
    {
        return this.all[ name ]
    },

    // set a cookie
    set: function(name, value, expires_seconds)
    {
        this.all[ name ] = value;

        if ( expires_seconds ) {
            ! this.timeout[ name ] || clearTimeout( this.timeout[ name ] )
            this.timeout[ name ] = setTimeout(() => this.unset(name), parseFloat(expires_seconds) * 1000)
        }
    },

    // delete a cookie
    unset: function(name)
    {
        delete this.all[ name ]
    }    
}

Here is how I ended up doing it in Jest:

My cookie wasn't added anymore to document.cookie after adding the Secure attribute (because document.location is the insecure http://localhost).

So after lots of trials and errors (trying to intercept document.cookie's setter and dropping its ;Secure from it, but then calling again document.cookie = with the new value triggered an infinite loop as it entered again the setter...), I ended up with this quite simple solution:

beforeEach(function() {
  let cookieJar = document.cookie;
  jest.spyOn(document, 'cookie', 'set').mockImplementation(cookie => {
    cookieJar += cookie;
  });
  jest.spyOn(document, 'cookie', 'get').mockImplementation(() => cookieJar);
})
Related