Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit-test an AngularJS service that uses localStorage internally

How do I unit-test a factory that uses $window.localStorage internally using Jasmine and ngMock?

Here's something similar to what I have:

myApp.factory('myData',function ($window) {
    return { 
        message: $window.localStorage['stuff'] 
    }
});

Thanks a bunch!

like image 295
tanmay Avatar asked Sep 01 '25 02:09

tanmay


2 Answers

The following works with Jasmine 2.4:

angular.module('MyModule', []).factory('myData',function ($window) {
    return { 
        message: function(){
          return $window.localStorage['stuff'] ;
        }
    }
});

describe("TestName", function() {
  beforeEach(module('MyModule'));
  var myData, store;
  beforeEach(inject(function(_myData_) {
    myData = _myData_;
    store = {};
    var localStorage = window.localStorage;
    spyOn(localStorage, 'getItem').and.callFake(function (key) {
      return store[key];
    });
    spyOn(localStorage, 'setItem').and.callFake(function (key, value) {
      return store[key] = value + '';
    });
    spyOn(localStorage, 'clear').and.callFake(function () {
      store = {};
    });
  }));

  it("feature desc", function() {
    localStorage['stuff'] = 'hello';
    expect(myData.message()).toEqual('hello');
  });
});

Note use of the _myData_ underscore trick (see docs).

like image 133
andyhasit Avatar answered Sep 02 '25 15:09

andyhasit


Small clarification: in case localStorage doesn't contain a key this mock return undefined, but real one returns null

The getItem(key) method must return the current value associated with the given key. If the given key does not exist in the list associated with the object then this method must return null. https://www.w3.org/TR/webstorage/

  spyOn(localStorage, 'getItem').and.callFake(function (key) {
    return store[key] !== undefined ? store[key] : null;
  });

Note: in the previous example method removeItem was not mocked

  spyOn(localStorage, 'removeItem').and.callFake(function (key, value) {
    delete store[key];
  });

P.S. I've found another way to write tests with local storage without mock it, probably it makes sense, working with primitives and localStorage could be tricky.

afterEach(() => {
  localStorage.removeItem(testTokenKey);
});
like image 30
Dzmitry Atkayey Avatar answered Sep 02 '25 16:09

Dzmitry Atkayey