0%

jest-localstorage-does-not-need-mock-now

You don’t need to mock localStorage with latest jsdom

Suppose you have a localStorage utility function like this:

1
2
3
4
5
6
7
8
9
10
11
12
// storage-utils.ts
export function saveItem(item: any) {
localStorage.setItem('item', JSON.stringify(item));
}

export function getItem(name: string) {
const jsonString = localStorage.getItem(name);
if (jsonString) {
return JSON.parse(jsonString);
}
return null;
}

To test above code, you have the following test file:

1
2
3
4
5
6
7
8
9
10
11
// storage-utils.spec.ts
import { getItem, saveItem } from './storage-utils';

describe('local storage should work', () => {
it('should set and get item', () => {
const item = { name: 'John Doe', age: 30 };
saveItem(item);
const storedItem = getItem('item');
expect(storedItem).toEqual(item);
});
});

When you run this test with Jest

1
2
# first, navigate to the directory where the test file is located.
jest storage-utils.spec.ts

Everything works fine, but localStorage is only available in browser environment, and you didn’t mock it, why?

This is because Jest now use jsdom as the default environment, and jsdom has implemented localStorage and sessionStorage APIs, so you don’t need to mock them anymore.

To test this, you can find the jest.config.js file in the root directory of your project, and add the following line to the testEnvironment property:

1
2
3
4
5
module.exports = {
preset: 'jest-preset-angular',
setupFilesAfterEnv: ['<rootDir>/setup-jest.ts'],
testEnvironment: 'node', // <-- change this to 'node'
};

Then run the test again, you will see the test failed because localStorage is not available in the node environment, and you will get the following error:

1
2
localStorage is not defined
ReferenceError: localStorage is not defined

Use jsdom by in test file

Suppose you global jest config is node(in file jest.config.js under project root), but you want to use jsdom in some test cases, you can use the following code in the test file, add @jest-environment jsdom at the top of the file.(Don’t use // comment, it will not work)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* @jest-environment jsdom
*/

import { getItem, saveItem } from './storage-utils';

describe('local storage should work', () => {
it('should set and get item', () => {
const item = { name: 'John Doe', age: 30 };
saveItem(item);
const storedItem = getItem('item');
expect(storedItem).toEqual(item);
});
});

Mock localStorage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// mockLocalStorage.ts
const mockLocalStorage = (() => {
let store = {} as Storage;

return {
getItem(key: string) {
return store[key];
},

setItem(key: string, value: string) {
store[key] = value;
},

removeItem(key: string) {
delete store[key];
},

clear() {
store = {} as Storage;
},
};
})();

export default mockLocalStorage;

Test file, note that we use globalThis here, it’s environment independently, and works both in node and browser environment.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// storage-utils.spec.ts
import { getItem, saveItem } from './storage-utils';
import mockLocalStorage from './mockLocalStorage';

describe('local storage should work', () => {
beforeEach(() => {
Object.defineProperty(globalThis, 'localStorage', {
value: mockLocalStorage,
});
});

it('should set and get item', () => {
const item = { name: 'John Doe', age: 30 };
saveItem(item);
const storedItem = getItem('item');
expect(storedItem).toEqual(item);
});
});

References:

  1. https://jestjs.io/docs/configuration#testenvironment-string - the official document still said that node is the default environment, but in my case, it’s jsdom, not sure why.
  2. https://github.com/jsdom/jsdom/blob/main/Changelog.md#11120