TIL: Mocking localStorage and sessionStorage in Angular Unit Tests

Armno P. 🇹🇭
3 min readMar 22, 2017

Today I learned how to mock localStorage and sessionStorage in a Service’s unit tests in Angular.

(Well, it’s not actually today that I learned this, but as a thing that I use in many projects and I still always forgot. It’s worth taking some notes 📝 so I will never forget this again … which of course, I will)

Here are the reference articles/posts I learned from

Big thanks to them!

I created a companion repo for this post on GitHub. You can check the code and try it out at armno/angular-mock-localstorage.

It is based on @angular/cli version 1.0.0-rc.4 and @angular/* version 2.4.10 . The code is also working with Angular 4.0.0-rc.1 .

The TokenService

Let’s say we have a service in an Angular app: TokenService which reads/writes a token into localStorage or sessionStorage on the browser. (From now on, I will refer to only localStorage as they both have similar APIs)

// token.service.tsimport { Injectable } from ‘@angular/core’;@Injectable()
export class TokenService {
private TOKEN_KEY = ‘id_token’; constructor() { } setAccessToken(token: string) {
localStorage.setItem(this.TOKEN_KEY, token);
}
getAccessToken(): string {
return localStorage.getItem(this.TOKEN_KEY);
}
}

In this Service’s unit tests, we don’t want it to use the real localStorage because our tests should be able to run independently from localStorage. We are only focusing on the Service itself, and not anything else.

This is a generated spec file of TokenService (via angular-cli)

// token.service.spec.tsimport { TestBed, inject } from '@angular/core/testing';import { TokenService } from './token.service';describe('TokenService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [TokenService]
});
});
it('should ...',
inject([TokenService], (service: TokenService) => {
expect(service).toBeTruthy();
}));
});

which I normally adapt a bit (and fix that should ... ) to

import { TestBed, inject } from '@angular/core/testing';import { TokenService } from './token.service';describe('TokenService', () => {
let service: TokenService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [TokenService]
});
service = TestBed.get(TokenService);
});
it('should create the service',
() => {
expect(service).toBeTruthy();
});
});

Creating mock localStorage

We can create a mock version of localStorage inside beforeEach() with an object with similar APIs to localStorage itself.

beforeEach(() => {
...
let store = {};
const mockLocalStorage = {
getItem: (key: string): string => {
return key in store ? store[key] : null;
},
setItem: (key: string, value: string) => {
store[key] = `${value}`;
},
removeItem: (key: string) => {
delete store[key];
},
clear: () => {
store = {};
}
};

});

Then we can use spyOn method with and.callFake for each mockLocalStorage object’s methods.

beforeEach(() => {
...
let store = {};
const mockLocalStorage = {
getItem: (key: string): string => {
return key in store ? store[key] : null;
},
setItem: (key: string, value: string) => {
store[key] = `${value}`;
},
removeItem: (key: string) => {
delete store[key];
},
clear: () => {
store = {};
}
};
spyOn(localStorage, 'getItem')
.and.callFake(mockLocalStorage.getItem);
spyOn(localStorage, 'setItem')
.and.callFake(mockLocalStorage.setItem);
spyOn(localStorage, 'removeItem')
.and.callFake(mockLocalStorage.removeItem);
spyOn(localStorage, 'clear')
.and.callFake(mockLocalStorage.clear);

});

This basically means: whenever localStorage.getItem is called, instead, call mockLocalStorage.getItem with the same arguments, and so on.

Note that .length property and key() method are not implemented. I personally never have to use them but there should be some examples online on how to also mock them. 😅

And finally, the tests:

describe('setAccessToken', () => {
it('should store the token in localStorage',
() => {
service.setAccessToken('sometoken');
expect(localStorage.getItem('id_token')).toEqual('sometoken');
});
});
describe('getAccessToken', () => {
it('should return stored token from localStorage',
() => {
localStorage.setItem('id_token', 'anothertoken');
expect(service.getAccessToken()).toEqual('anothertoken');
});
});

This is the pattern I’ve been using for all of my Angular projects so far. Of course, feedback is welcome. 🙏

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Responses (6)

Write a response

let store = {};
const mockLocalStorage = {
getItem: (key: string): string => {
return key in storage ? storage[key] : null;
},
setItem: (key: string, value: string) ...

You have a typo friend, it’s not storage but store :)

thanks a lot ;)

I have done similar in my spec file but it randomly fails says sessionStorage.setItem is not a function.
It doesn't fail always. But out of 5 runs it fails once or twice. Here is my code, any suggestions please?
import { TestBed, waitForAsync } from…

Recommended from Medium

Lists

See more recommendations