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. 🙏

--

--