我一直在Jest测试中得到“localStorage未定义”这有意义,但我的选择是什么?撞砖墙。
答案 0 :(得分:115)
答案 1 :(得分:76)
在此帮助下计算出来:https://groups.google.com/forum/#!topic/jestjs/9EPhuNWVYTg
使用以下内容设置文件:
var localStorageMock = (function() {
var store = {};
return {
getItem: function(key) {
return store[key];
},
setItem: function(key, value) {
store[key] = value.toString();
},
clear: function() {
store = {};
},
removeItem: function(key) {
delete store[key];
}
};
})();
Object.defineProperty(window, 'localStorage', { value: localStorageMock });
然后在Jest configs下将以下行添加到package.json
"setupTestFrameworkScriptFile":"PATH_TO_YOUR_FILE",
答案 2 :(得分:44)
如果使用create-react-app,documentation中会解释一个更简单直接的解决方案。
创建src/setupTests.js
并将其放入其中:
const localStorageMock = {
getItem: jest.fn(),
setItem: jest.fn(),
clear: jest.fn()
};
global.localStorage = localStorageMock;
Tom Mertz在以下评论中的贡献:
然后您可以通过执行类似
的操作来测试您的localStorageMock函数是否被使用expect(localStorage.getItem).toBeCalledWith('token')
// or
expect(localStorage.getItem.mock.calls.length).toBe(1)
如果你想确保它被调用,你的测试内部。查看https://facebook.github.io/jest/docs/en/mock-functions.html
答案 3 :(得分:13)
答案 4 :(得分:12)
当前(19年1月)不能像平常一样由玩笑来嘲笑或监视localStorage,正如create-react-app文档中所述。这是由于jsdom中所做的更改。您可以在https://github.com/facebook/jest/issues/6798和https://github.com/jsdom/jsdom/issues/2318这里阅读。
作为一种解决方法,您可以代替侦探原型:
// does not work:
jest.spyOn(localStorage, "setItem");
localStorage.setItem = jest.fn();
// works:
jest.spyOn(window.localStorage.__proto__, 'setItem');
window.localStorage.__proto__.setItem = jest.fn();
// assertions as usual:
expect(localStorage.setItem).toHaveBeenCalled();
答案 5 :(得分:11)
处理undefined
值的更好的替代方案(它没有toString()
)并且如果值不存在则返回null
。使用react
v15,redux
和redux-auth-wrapper
class LocalStorageMock {
constructor() {
this.store = {}
}
clear() {
this.store = {}
}
getItem(key) {
return this.store[key] || null
}
setItem(key, value) {
this.store[key] = value
}
removeItem(key) {
delete this.store[key]
}
}
global.localStorage = new LocalStorageMock
答案 6 :(得分:9)
不幸的是,我在这里找到的解决方案对我不起作用。
所以我正在研究Jest GitHub问题,并发现了这个thread
最受好评的解决方案是:
const spy = jest.spyOn(Storage.prototype, 'setItem');
// or
Storage.prototype.getItem = jest.fn(() => 'bla');
答案 7 :(得分:3)
我想我会在带有 React 的 TypeScript 中添加另一个非常适合我的解决方案:
我创建了一个 mockLocalStorage.ts
export const mockLocalStorage = () => {
const setItemMock = jest.fn();
const getItemMock = jest.fn();
beforeEach(() => {
Storage.prototype.setItem = setItemMock;
Storage.prototype.getItem = getItemMock;
});
afterEach(() => {
setItemMock.mockRestore();
getItemMock.mockRestore();
});
return { setItemMock, getItemMock };
};
我的组件:
export const Component = () => {
const foo = localStorage.getItem('foo')
return <h1>{foo}</h1>
}
然后在我的测试中我像这样使用它:
import React from 'react';
import { mockLocalStorage } from '../../test-utils';
import { Component } from './Component';
const { getItemMock, setItemMock } = mockLocalStorage();
it('fetches something from localStorage', () => {
getItemMock.mockReturnValue('bar');
render(<Component />);
expect(getItemMock).toHaveBeenCalled();
expect(getByText(/bar/i)).toBeInTheDocument()
});
it('expects something to be set in localStorage' () => {
const value = "value"
const key = "key"
render(<Component />);
expect(setItemMock).toHaveBeenCalledWith(key, value);
}
答案 8 :(得分:3)
您需要使用此代码段模拟本地存储
// localStorage.js
var localStorageMock = (function() {
var store = {};
return {
getItem: function(key) {
return store[key] || null;
},
setItem: function(key, value) {
store[key] = value.toString();
},
clear: function() {
store = {};
}
};
})();
Object.defineProperty(window, 'localStorage', {
value: localStorageMock
});
在玩笑的配置中:
"setupFiles":["localStorage.js"]
随便问什么。
答案 9 :(得分:2)
正如@ ck4建议documentation明确解释在jest中使用localStorage
。但是模拟函数未能执行任何localStorage
方法。
下面是我的反应组件的详细示例,它使用抽象方法来编写和读取数据,
//file: storage.js
const key = 'ABC';
export function readFromStore (){
return JSON.parse(localStorage.getItem(key));
}
export function saveToStore (value) {
localStorage.setItem(key, JSON.stringify(value));
}
export default { readFromStore, saveToStore };
错误:的
TypeError: _setupLocalStorage2.default.setItem is not a function
<强>修正:强>
为jest添加以下模拟函数(路径:.jest/mocks/setUpStore.js
)
let mockStorage = {};
module.exports = window.localStorage = {
setItem: (key, val) => Object.assign(mockStorage, {[key]: val}),
getItem: (key) => mockStorage[key],
clear: () => mockStorage = {}
};
代码段引自here
答案 10 :(得分:2)
我从github找到了这个解决方案
var localStorageMock = (function() {
var store = {};
return {
getItem: function(key) {
return store[key] || null;
},
setItem: function(key, value) {
store[key] = value.toString();
},
clear: function() {
store = {};
}
};
})();
Object.defineProperty(window, 'localStorage', {
value: localStorageMock
});
您可以将此代码插入setupTests中,它应该可以正常工作。
我在一个类型为ctipt的项目中对其进行了测试。
答案 11 :(得分:1)
使用 TypeScript 和 Jest 的更优雅的解决方案。
interface Spies {
[key: string]: jest.SpyInstance
}
describe('→ Local storage', () => {
const spies: Spies = {}
beforeEach(() => {
['setItem', 'getItem', 'clear'].forEach((fn: string) => {
const mock = jest.fn(localStorage[fn])
spies[fn] = jest.spyOn(Storage.prototype, fn).mockImplementation(mock)
})
})
afterEach(() => {
Object.keys(spies).forEach((key: string) => spies[key].mockRestore())
})
test('→ setItem ...', async () => {
localStorage.setItem( 'foo', 'bar' )
expect(localStorage.getItem('foo')).toEqual('bar')
expect(spies.setItem).toHaveBeenCalledTimes(1)
})
})
答案 12 :(得分:1)
要在Typescript中执行相同操作,请执行以下操作:
设置具有以下内容的文件:
let localStorageMock = (function() {
let store = new Map()
return {
getItem(key: string):string {
return store.get(key);
},
setItem: function(key: string, value: string) {
store.set(key, value);
},
clear: function() {
store = new Map();
},
removeItem: function(key: string) {
store.delete(key)
}
};
})();
Object.defineProperty(window, 'localStorage', { value: localStorageMock });
然后将以下行添加到Jest配置下的package.json中
"setupTestFrameworkScriptFile":"PATH_TO_YOUR_FILE",
或者您可以在要模拟本地存储的测试用例中导入此文件。
答案 13 :(得分:1)
如果您正在寻找模拟而不是存根,这是我使用的解决方案:
export const localStorageMock = {
getItem: jest.fn().mockImplementation(key => localStorageItems[key]),
setItem: jest.fn().mockImplementation((key, value) => {
localStorageItems[key] = value;
}),
clear: jest.fn().mockImplementation(() => {
localStorageItems = {};
}),
removeItem: jest.fn().mockImplementation((key) => {
localStorageItems[key] = undefined;
}),
};
export let localStorageItems = {}; // eslint-disable-line import/no-mutable-exports
我导出存储项目以便于初始化。即我可以轻松地将其设置为对象
在Jest + JSm的较新版本中,无法进行设置,但是localstorage已经可用,您可以像这样监视它:
const setItemSpy = jest.spyOn(Object.getPrototypeOf(window.localStorage), 'setItem');
答案 14 :(得分:1)
在这里摒弃其他一些答案来解决一个带有Typescript的项目。我创建了一个像这样的LocalStorageMock:
export class LocalStorageMock {
private store = {}
clear() {
this.store = {}
}
getItem(key: string) {
return this.store[key] || null
}
setItem(key: string, value: string) {
this.store[key] = value
}
removeItem(key: string) {
delete this.store[key]
}
}
然后我创建了一个LocalStorageWrapper类,用于对应用程序中的本地存储进行所有访问,而不是直接访问全局本地存储变量。可以很容易地在包装器中设置模拟以进行测试。
答案 15 :(得分:0)
您可以使用这种方法来避免嘲笑。
Storage.prototype.getItem = jest.fn(() => expectedPayload);
答案 16 :(得分:0)
describe('getToken', () => {
const Auth = new AuthService();
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Ik1yIEpvc2VwaCIsImlkIjoiNWQwYjk1Mzg2NTVhOTQ0ZjA0NjE5ZTA5IiwiZW1haWwiOiJ0cmV2X2pvc0Bob3RtYWlsLmNvbSIsInByb2ZpbGVVc2VybmFtZSI6Ii9tcmpvc2VwaCIsInByb2ZpbGVJbWFnZSI6Ii9Eb3Nlbi10LUdpci1sb29rLWN1dGUtbnVrZWNhdDMxNnMtMzExNzAwNDYtMTI4MC04MDAuanBnIiwiaWF0IjoxNTYyMzE4NDA0LCJleHAiOjE1OTM4NzYwMDR9.YwU15SqHMh1nO51eSa0YsOK-YLlaCx6ijceOKhZfQZc';
beforeEach(() => {
global.localStorage = jest.fn().mockImplementation(() => {
return {
getItem: jest.fn().mockReturnValue(token)
}
});
});
it('should get the token from localStorage', () => {
const result = Auth.getToken();
expect(result).toEqual(token);
});
});
创建一个模拟并将其添加到global
对象中
答案 17 :(得分:0)
以下解决方案与更严格的TypeScript,ESLint,TSLint和Prettier配置兼容:{ "proseWrap": "always", "semi": false, "singleQuote": true, "trailingComma": "es5" }
:
class LocalStorageMock {
public store: {
[key: string]: string
}
constructor() {
this.store = {}
}
public clear() {
this.store = {}
}
public getItem(key: string) {
return this.store[key] || undefined
}
public setItem(key: string, value: string) {
this.store[key] = value.toString()
}
public removeItem(key: string) {
delete this.store[key]
}
}
/* tslint:disable-next-line:no-any */
;(global as any).localStorage = new LocalStorageMock()
HT / https://stackoverflow.com/a/51583401/101290,了解如何更新global.localStorage
答案 18 :(得分:-1)
这对我有用,
delete global.localStorage;
global.localStorage = {
getItem: () =>
}