Sigo recibiendo "localStorage no está definido" en las pruebas de Jest, lo que tiene sentido, pero ¿cuáles son mis opciones? Golpear paredes de ladrillo.
Gran solución de @chiedo
Sin embargo, usamos la sintaxis ES2015 y sentí que era un poco más limpio escribirlo de esta manera.
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;
Lo descubrí con ayuda de esto: https://groups.google.com/forum/#!topic/jestjs/9EPhuNWVYTg
Configure un archivo con los siguientes contenidos:
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 });
Luego agrega la siguiente línea a su package.json en sus configuraciones de Jest
"setupTestFrameworkScriptFile":"PATH_TO_YOUR_FILE",
"setupFiles": [...]funciona igual de bien Con la opción de matriz, permite separar simulacros en archivos separados. Por ejemplo:"setupFiles": ["<rootDir>/__mocks__/localStorageMock.js"]
getItemdifiere ligeramente de lo que devolvería un navegador si los datos no se configuran con una clave específica. llamar getItem("foo")cuando no está configurado, por ejemplo, volverá nullen un navegador, pero undefinedcon este simulacro, esto estaba causando que fallara una de mis pruebas. La solución simple para mí fue regresar store[key] || nulla la getItemfunción
localStorage['test'] = '123'; localStorage.getItem('test')
Si usa create-react-app, hay una solución más simple y directa explicada en la documentación .
Crea src/setupTests.jsy pon esto en él:
const localStorageMock = {
getItem: jest.fn(),
setItem: jest.fn(),
clear: jest.fn()
};
global.localStorage = localStorageMock;
Contribución de Tom Mertz en un comentario a continuación:
Luego puede probar que las funciones de su LocalStorageMock se usan haciendo algo como
expect(localStorage.getItem).toBeCalledWith('token')
// or
expect(localStorage.getItem.mock.calls.length).toBe(1)
dentro de sus pruebas si quería asegurarse de que se llamara. Echa un vistazo a https://facebook.github.io/jest/docs/en/mock-functions.html
localStorageque usa en su código. (si usa create-react-appy todos los scripts automáticos que proporciona naturalmente)
expect(localStorage.getItem).toBeCalledWith('token')o expect(localStorage.getItem.mock.calls.length).toBe(1)dentro de sus pruebas si desea asegurarse de que se haya llamado. Echa un vistazo a facebook.github.io/jest/docs/en/mock-functions.html
localStorage? ¿No le gustaría reiniciar los espías después de cada prueba para evitar el "derrame" en otras pruebas?
Actualmente (Oct '19) localStorage no puede ser burlado o espiado por broma como lo haría normalmente, y como se describe en los documentos de create-react-app. Esto se debe a los cambios realizados en jsdom. Puedes leer sobre esto en broma y jsdom rastreadores de problemas de .
Como solución alternativa, puede espiar el prototipo en su lugar:
// 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');
o simplemente tomas un paquete simulado como este:
https://www.npmjs.com/package/jest-localstorage-mock
no solo maneja la funcionalidad de almacenamiento, sino que también le permite probar si realmente se llamó a la tienda.
Una mejor alternativa que maneja undefinedvalores (no tiene toString()) y devuelve nullsi el valor no existe. reactProbé esto con v15, reduxyredux-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
Si está buscando un simulacro y no un trozo, aquí está la solución que uso:
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
Exporto los elementos de almacenamiento para una fácil inicialización. IE puedo configurarlo fácilmente en un objeto
En las versiones más recientes de Jest + JSDom no es posible configurar esto, pero el almacenamiento local ya está disponible y puede espiarlo de esta manera:
const setItemSpy = jest.spyOn(Object.getPrototypeOf(window.localStorage), 'setItem');
Encontré esta solución de 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
});
Puede insertar este código en sus SetupTests y debería funcionar bien.
Lo probé en un proyecto con typectipt.
Desafortunadamente, las soluciones que he encontrado aquí no funcionaron para mí.
Así que estaba mirando los problemas de Jest GitHub y encontré este hilo
Las soluciones más votadas fueron estas:
const spy = jest.spyOn(Storage.prototype, 'setItem');
// or
Storage.prototype.getItem = jest.fn(() => 'bla');
windowni están Storagedefinidas. Tal vez es la versión anterior de Jest que estoy usando.
Como @ ck4 la documentación sugerida tiene una explicación clara para usar localStorageen broma. Sin embargo, las funciones simuladas no podían ejecutar ninguna de laslocalStorage métodos.
A continuación se muestra el ejemplo detallado de mi componente de reacción que utiliza métodos abstractos para escribir y leer datos,
//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 };
Error:
TypeError: _setupLocalStorage2.default.setItem is not a function
Solución:
Añadir a continuación la función simulacro de broma (ruta: .jest/mocks/setUpStore.js)
let mockStorage = {};
module.exports = window.localStorage = {
setItem: (key, val) => Object.assign(mockStorage, {[key]: val}),
getItem: (key) => mockStorage[key],
clear: () => mockStorage = {}
};
Se hace referencia al fragmento desde aquí
Riffed algunas otras respuestas aquí para resolverlo para un proyecto con Typecript. Creé un LocalStorageMock como este:
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]
}
}
Luego creé una clase LocalStorageWrapper que uso para todo el acceso al almacenamiento local en la aplicación en lugar de acceder directamente a la variable de almacenamiento local global. Facilitó la configuración del simulacro en el contenedor para las pruebas.
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);
});
});
Crea un simulacro y globalagrégalo al objeto
Puedes usar este enfoque para evitar burlarte.
Storage.prototype.getItem = jest.fn(() => expectedPayload);
Debes burlarte del almacenamiento local con estos fragmentos
// 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
});
Y en la configuración de broma:
"setupFiles":["localStorage.js"]
Siéntase libre de preguntar cualquier cosa.
La siguiente solución es compatible para probar con configuraciones TypeScript, ESLint, TSLint y Prettier más estrictas { "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 sobre cómo actualizar global.localStorage
Para hacer lo mismo en el mecanografiado, haga lo siguiente:
Configure un archivo con los siguientes contenidos:
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 });
Luego agrega la siguiente línea a su package.json en sus configuraciones de Jest
"setupTestFrameworkScriptFile":"PATH_TO_YOUR_FILE",
O importa este archivo en su caso de prueba donde desea burlarse del almacenamiento local.
Esto funcionó para mí
delete global.localStorage;
global.localStorage = {
getItem: () =>
}
value + ''en el setter para manejar correctamente los valores nulos e indefinidos|| null, por eso mi prueba estaba fallando, porque en mi prueba estaba usandonot.toBeDefined(). La solución @Chiedo lo hace funcionar de nuevo