How to mock localStorage in JavaScript unit tests?

前端 未结 14 1548
南笙
南笙 2020-11-29 18:46

Are there any libraries out there to mock localStorage?

I\'ve been using Sinon.JS for most of my other javascript mocking and have found it is really gr

相关标签:
14条回答
  • 2020-11-29 19:23

    Are there any libraries out there to mock localStorage?

    I just wrote one:

    (function () {
        var localStorage = {};
        localStorage.setItem = function (key, val) {
             this[key] = val + '';
        }
        localStorage.getItem = function (key) {
            return this[key];
        }
        Object.defineProperty(localStorage, 'length', {
            get: function () { return Object.keys(this).length - 2; }
        });
    
        // Your tests here
    
    })();
    

    My initial testing shows that localStorage refuses to be assignable in firefox

    Only in global context. With a wrapper function as above, it works just fine.

    0 讨论(0)
  • 2020-11-29 19:29

    credits to https://medium.com/@armno/til-mocking-localstorage-and-sessionstorage-in-angular-unit-tests-a765abdc9d87 Make a fake localstorage, and spy on localstorage, when it is caleld

     beforeAll( () => {
        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);
      })
    

    And here we use it

    it('providing search value should return matched item', () => {
        localStorage.setItem('defaultLanguage', 'en-US');
    
        expect(...
      });
    
    0 讨论(0)
  • 2020-11-29 19:30

    This is how I like to do it. Keeps it simple.

      let localStoreMock: any = {};
    
      beforeEach(() => {
    
        angular.mock.module('yourApp');
    
        angular.mock.module(function ($provide: any) {
    
          $provide.service('localStorageService', function () {
            this.get = (key: any) => localStoreMock[key];
            this.set = (key: any, value: any) => localStoreMock[key] = value;
          });
    
        });
      });
    
    0 讨论(0)
  • 2020-11-29 19:31

    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');
    
    0 讨论(0)
  • 2020-11-29 19:31

    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

    0 讨论(0)
  • 2020-11-29 19:33

    I found that I did not need to mock it. I could change the actual local storage to the state I wanted it via setItem, then just query the values to see if it changed via getItem. It's not quite as powerful as mocking as you can't see how many times something was changed, but it worked for my purposes.

    0 讨论(0)
提交回复
热议问题