Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock localStorage in JavaScript unit tests?

People also ask

How do I test local storage?

Using Google Chrome, click on menu -> Tools -> Developer Tools. Then under Resources you will see 'Local Storage' and 'Web Storage'. Using Firefox with the Firebug add on you can easily inspect the localStorage/sessionStorage object in the DOM tab.

Does jest mock localStorage?

In short: localStorage is actually an instance of a Storage class. Sadly, mocking methods on a class instance (i.e. localStorage. getItem ) doesn't work with our jest.

How do I see localStorage in JavaScript?

It's simple. Just go to the developer tools by pressing F12 , then go to the Application tab. In the Storage section expand Local Storage. After that, you'll see all your browser's local storage there.


Here is a simple way to mock it with Jasmine:

let localStore;

beforeEach(() => {
  localStore = {};

  spyOn(window.localStorage, 'getItem').and.callFake((key) =>
    key in localStore ? localStore[key] : null
  );
  spyOn(window.localStorage, 'setItem').and.callFake(
    (key, value) => (localStore[key] = value + '')
  );
  spyOn(window.localStorage, 'clear').and.callFake(() => (localStore = {}));
});

If you want to mock the local storage in all your tests, declare the beforeEach() function shown above in the global scope of your tests (the usual place is a specHelper.js script).


just mock the global localStorage / sessionStorage (they have the same API) for your needs.
For example:

 // Storage Mock
  function storageMock() {
    let storage = {};

    return {
      setItem: function(key, value) {
        storage[key] = value || '';
      },
      getItem: function(key) {
        return key in storage ? storage[key] : null;
      },
      removeItem: function(key) {
        delete storage[key];
      },
      get length() {
        return Object.keys(storage).length;
      },
      key: function(i) {
        const keys = Object.keys(storage);
        return keys[i] || null;
      }
    };
  }

And then what you actually do, is something like that:

// mock the localStorage
window.localStorage = storageMock();
// mock the sessionStorage
window.sessionStorage = storageMock();

Also consider the option to inject dependencies in an object's constructor function.

var SomeObject(storage) {
  this.storge = storage || window.localStorage;
  // ...
}

SomeObject.prototype.doSomeStorageRelatedStuff = function() {
  var myValue = this.storage.getItem('myKey');
  // ...
}

// In src
var myObj = new SomeObject();

// In test
var myObj = new SomeObject(mockStorage)

In line with mocking and unit testing, I like to avoid testing the storage implementation. For instance no point in checking if length of storage increased after you set an item, etc.

Since it is obviously unreliable to replace methods on the real localStorage object, use a "dumb" mockStorage and stub the individual methods as desired, such as:

var mockStorage = {
  setItem: function() {},
  removeItem: function() {},
  key: function() {},
  getItem: function() {},
  removeItem: function() {},
  length: 0
};

// Then in test that needs to know if and how setItem was called
sinon.stub(mockStorage, 'setItem');
var myObj = new SomeObject(mockStorage);

myObj.doSomeStorageRelatedStuff();
expect(mockStorage.setItem).toHaveBeenCalledWith('myKey');

The current solutions will not work in Firefox. This is because localStorage is defined by the html spec as being not modifiable. You can however get around this by accessing localStorage's prototype directly.

The cross browser solution is to mock the objects on Storage.prototype e.g.

instead of spyOn(localStorage, 'setItem') use

spyOn(Storage.prototype, 'setItem')
spyOn(Storage.prototype, 'getItem')

taken from bzbarsky and teogeos's replies here https://github.com/jasmine/jasmine/issues/299


This is what I do...

var mock = (function() {
  var store = {};
  return {
    getItem: function(key) {
      return store[key];
    },
    setItem: function(key, value) {
      store[key] = value.toString();
    },
    clear: function() {
      store = {};
    }
  };
})();

Object.defineProperty(window, 'localStorage', { 
  value: mock,
});