what is the best way to mock window.sessionStorage in jest

18,006

Solution 1

Here is the solution only use jestjs and typescript, nothing more.

index.ts:

export function getUserInfo() {
  const userInfo = window.sessionStorage.getItem('userInfo');
  if (userInfo) {
    return JSON.parse(userInfo);
  }
  return {};
}

index.spec.ts:

import { getUserInfo } from './';

const localStorageMock = (() => {
  let store = {};

  return {
    getItem(key) {
      return store[key] || null;
    },
    setItem(key, value) {
      store[key] = value.toString();
    },
    removeItem(key) {
      delete store[key];
    },
    clear() {
      store = {};
    }
  };
})();

Object.defineProperty(window, 'sessionStorage', {
  value: localStorageMock
});

describe('getUserInfo', () => {
  beforeEach(() => {
    window.sessionStorage.clear();
    jest.restoreAllMocks();
  });
  it('should get user info from session storage', () => {
    const getItemSpy = jest.spyOn(window.sessionStorage, 'getItem');
    window.sessionStorage.setItem('userInfo', JSON.stringify({ userId: 1, userEmail: '[email protected]' }));
    const actualValue = getUserInfo();
    expect(actualValue).toEqual({ userId: 1, userEmail: '[email protected]' });
    expect(getItemSpy).toBeCalledWith('userInfo');
  });

  it('should get empty object if no user info in session storage', () => {
    const getItemSpy = jest.spyOn(window.sessionStorage, 'getItem');
    const actualValue = getUserInfo();
    expect(actualValue).toEqual({});
    expect(window.sessionStorage.getItem).toBeCalledWith('userInfo');
    expect(getItemSpy).toBeCalledWith('userInfo');
  });
});

Unit test result with 100% coverage report:

 PASS  src/stackoverflow/51566816/index.spec.ts
  getUserInfo
    ✓ should get user info from session storage (6ms)
    ✓ should get empty object if no user info in session storage (1ms)

----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |      100 |      100 |      100 |      100 |                   |
 index.ts |      100 |      100 |      100 |      100 |                   |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        4.548s, estimated 6s

Here is the completed demo: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/51566816

Solution 2

You probably do not even need a mock. Just use window.sessionStorage as usual and write your condition based on window.sessionStorage.getItem(...) result instead of spying window.sessionStorage.setItem . Simply don't forget to call window.sessionStorage.clear() in beforeEach as demonstrated.

From Eric Burel's comment

Solution 3

This works for me along with adding object:

defineProperty(window, 'sessionStorage', {
   writable: true,
   configurable: true,
   value: localStorageMock
}
Share:
18,006

Related videos on Youtube

LHE
Author by

LHE

Updated on March 10, 2022

Comments

  • LHE
    LHE about 2 years

    Below is a very simple jest unit test and when running it, you will get error like

    Cannot spyOn on a primitive value; undefined given

    TypeError: Cannot read property 'getItem' of undefined

    but according to the last two comments of this post, localStorage and sessionStorage were already added to latest JSDOM and jest. If using jest-localstorage-mock and add it to my jest setupFiles then you will see weird error like

    TypeError: object[methodName].mockImplementation is not a function

    So my question is what's the best way to mock localStorage/sessionStorage in jest. Thanks

    describe('window.sessionStorage', () => {
        let mockSessionStorage;
        beforeEach(() => {
            mockSessionStorage = {};
            jest.spyOn(window.sessionStorage, "getItem").mockImplementation(key => {
                return mockSessionStorage[key];
            });
        });
    
        describe('getItem-', () => {
            beforeEach(() => {
                mockSessionStorage = {
                    foo: 'bar',
                }
            });
    
            it('gets string item', () => {
                const ret = window.sessionStorage.getItem('foo');
                expect(ret).toBe('bar');
            });
        });
    });
    

    Below is my jest config

    module.exports = {
        verbose: true,
        //setupFiles: ["jest-localstorage-mock"],
        testURL: "http://localhost/"
    };
    
  • Saksham
    Saksham over 3 years
    anything to be done to restore the original behavior of session storage at the end? Will this mock be valid only for the current test file and won't leak globally?
  • Eric Burel
    Eric Burel over 3 years
    You probably do not even need a mock. Just use window.sessionStorage as usual and write your condition based on window.sessionStorage.getItem(...) result instead of spying window.sessionStorage.setItem . Simply don't forget to call window.sessionStorage.clear() in beforeEach as demonstrated.
  • Oh Xyz
    Oh Xyz over 3 years
    @EricBurel This should be a good valid answer.
  • Alan Yong
    Alan Yong over 2 years
    return ReferenceError: window is not defined