Я продолжаю получать сообщение «localStorage is not defined» в тестах Jest, что имеет смысл, но каковы мои варианты? Удар по кирпичным стенам.
Ответы:
Отличное решение от @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 });
Затем вы добавляете следующую строку в свой package.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')
При использовании создания реагирующего-приложения, есть более простое и простое решение описано в документации .
Создайте 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
? Разве вы не хотели бы сбрасывать шпионов после каждого теста, чтобы предотвратить «перетекание» в другие тесты?
В настоящее время (октябрь '19) localStorage нельзя высмеивать или шпионить с помощью шуток, как это обычно делается и как указано в документации create-response-app. Это связано с изменениями, внесенными в jsdom. Вы можете прочитать об этом в трекерах проблем jest и 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');
Лучшая альтернатива, которая обрабатывает 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
или вы просто возьмете такой макет пакета:
https://www.npmjs.com/package/jest-localstorage-mock
он обрабатывает не только функции хранилища, но также позволяет проверить, действительно ли было вызвано хранилище.
Если вы ищете макет, а не заглушку, вот решение, которое я использую:
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');
К сожалению, решения, которые я здесь нашел, у меня не сработали.
Итак, я смотрел на проблемы Jest GitHub и нашел эту ветку
Наибольшее количество голосов получили следующие решения:
const spy = jest.spyOn(Storage.prototype, 'setItem');
// or
Storage.prototype.getItem = jest.fn(() => 'bla');
window
или Storage
любой. Возможно, я использую старую версию Jest.
Я нашел это решение с 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.
Как @ 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 = {}
};
Ссылка на фрагмент отсюда
Вы можете использовать этот подход, чтобы избежать насмешек.
Storage.prototype.getItem = jest.fn(() => expectedPayload);
Вам нужно имитировать локальное хранилище с помощью этих фрагментов
// 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"]
Не стесняйтесь спрашивать о чем угодно.
Отказался от некоторых других ответов здесь, чтобы решить эту проблему для проекта с 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
объекту
Чтобы сделать то же самое в машинописном тексте, сделайте следующее:
Установите файл со следующим содержимым:
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 });
Затем вы добавляете следующую строку в свой package.json в конфигурациях Jest
"setupTestFrameworkScriptFile":"PATH_TO_YOUR_FILE",
Или вы импортируете этот файл в свой тестовый пример, где хотите имитировать локальное хранилище.
Следующее решение совместимо для тестирования с более строгими конфигурациями 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
value + ''
в инкубаторе с ручкой нуль и неопределенными значениями правильно