Відповіді:
Чудове рішення від @chiedo
Однак ми використовуємо синтаксис ES2015, і я вважав, що це трохи чистіше написати це таким чином.
class LocalStorageMock {
constructor() {
this.store = {};
}
clear() {
this.store = {};
}
getItem(key) {
return this.store[key] || null;
}
setItem(key, value) {
this.store[key] = value.toString();
}
removeItem(key) {
delete this.store[key];
}
};
global.localStorage = new LocalStorageMock;
|| nullте, що тому мій тест виявився невдалим, тому що в моєму тесті я використовував not.toBeDefined(). Рішення @Chiedo змусить його працювати знову
З'ясував це за допомогою цього: 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 });
Потім ви додасте наступний рядок до свого пакета.json під вашими конфігураціями Jest
"setupTestFrameworkScriptFile":"PATH_TO_YOUR_FILE",
"setupFiles": [...]працює також. За допомогою параметра масиву дозволяє розділяти макети на окремі файли. Напр .:"setupFiles": ["<rootDir>/__mocks__/localStorageMock.js"]
getItemдещо відрізняється від того, що було б повернуто браузером, якщо дані не встановлені проти конкретного ключа. виклик, getItem("foo")коли його не встановлено, наприклад, повернеться nullу браузері, але undefinedцим макетом - це спричинило збій одного з моїх тестів. Просте рішення для мене було повернутися store[key] || nullв getItemфункції
localStorage['test'] = '123'; localStorage.getItem('test')
Якщо ви використовуєте create-react-app, в документації пояснюється більш просте і зрозуміле рішення .
Створіть src/setupTests.jsі вкладіть це:
const localStorageMock = {
getItem: jest.fn(),
setItem: jest.fn(),
clear: jest.fn()
};
global.localStorage = localStorageMock;
Вклад Тома Мерца в коментарі нижче:
Потім ви можете перевірити, чи використовуються функції вашого 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
localStorageви використовуєте у своєму коді. (якщо ви використовуєте create-react-appі всі автоматичні сценарії, які він надає природно)
expect(localStorage.getItem).toBeCalledWith('token')або expect(localStorage.getItem.mock.calls.length).toBe(1)всередині тестів, якщо ви хочете переконатися, що воно викликано. Перевірте facebook.github.io/jest/docs/en/mock-functions.html
localStorage? Чи не хочете ви скидати шпигунів після кожного тесту, щоб запобігти «переливанню» на інші тести?
В даний час (жовтень 1919 р.) Місцевим сховищем не можна глузувати або шпигувати на жарт, як зазвичай, як це було зазначено в документах "create-react-app". Це пов’язано зі змінами, внесеними в jsdom. Ви можете прочитати про це в жарт і jsdom трекерів випуску.
Для вирішення цього питання ви можете шпигувати за прототипом:
// 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();
jest.spyOn(window.localStorage.__proto__, 'setItem');
або ти просто береш такий макет-пакет:
https://www.npmjs.com/package/jest-localstorage-mock
він обробляє не тільки функціональність пам’яті, але також дозволяє перевірити, чи справді був викликаний магазин.
Краща альтернатива, яка обробляє undefinedзначення (у них немає toString()) і повертає, nullякщо значення не існує. Випробували це за допомогою reactv15 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
removeItem: developer.mozilla.org/en-US/docs/Web/API/Storage/removeItem
Якщо ви шукаєте макет, а не заглушку, ось таке рішення я використовую:
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
Я експортую елементи зберігання для легкої ініціалізації. IE Я можу легко встановити його на об'єкт
У нових версіях Jest + JSDom це неможливо встановити, але локальне зберігання вже доступне, і ви можете шпигувати за ним так:
const setItemSpy = jest.spyOn(Object.getPrototypeOf(window.localStorage), 'setItem');
Я знайшов це рішення у 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, і він повинен справно працювати.
Я перевірив це в проекті з typectipt.
На жаль, рішення, які я знайшов тут, не працювали для мене.
Тому я переглядав проблеми Jest GitHub і знайшов цю тему
Найбільш актуальні рішення були такі:
const spy = jest.spyOn(Storage.prototype, 'setItem');
// or
Storage.prototype.getItem = jest.fn(() => 'bla');
windowабо не Storageвизначаються. Можливо, це стара версія Jest, яку я використовую.
Як @ ck4 запропонована документація має чітке пояснення для використання 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/mocks/setUpStore.js)
let mockStorage = {};
module.exports = window.localStorage = {
setItem: (key, val) => Object.assign(mockStorage, {[key]: val}),
getItem: (key) => mockStorage[key],
clear: () => mockStorage = {}
};
Тут викладені деякі інші відповіді, щоб вирішити це для проекту з 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, який використовую для доступу до локальної пам’яті в додатку, а не для прямого доступу до глобальної змінної локального зберігання. Це дозволило легко встановити макет в обгортці для тестів.
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);
});
});
Створіть макет і додайте його до globalobjectt
Потрібно знущатися над місцевим сховищем за допомогою цих фрагментів
// 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
});
І в конфігурації vic:
"setupFiles":["localStorage.js"]
Сміливо запитайте що-небудь.
Наступне рішення сумісне для тестування з більш жорсткими TypeScript, ESLint, TSLint та Prettier config { "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
Щоб зробити те ж саме в 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 });
Потім ви додасте наступний рядок до свого пакета.json під вашими конфігураціями Jest
"setupTestFrameworkScriptFile":"PATH_TO_YOUR_FILE",
Або ви імпортуєте цей файл у свій тестовий випадок, коли ви хочете знущатися над локальним сховищем.
value + ''в сетері, щоб правильно обробити нульові та невизначені значення