Відповіді:
Чудове рішення від @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
якщо значення не існує. Випробували це за допомогою 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
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);
});
});
Створіть макет і додайте його до global
objectt
Потрібно знущатися над місцевим сховищем за допомогою цих фрагментів
// 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 + ''
в сетері, щоб правильно обробити нульові та невизначені значення