Respuestas:
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;
|| null
, por eso mi prueba estaba fallando, porque en mi prueba estaba usando not.toBeDefined()
. La solución @Chiedo lo hace funcionar de nuevo
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"]
getItem
difiere 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á null
en un navegador, pero undefined
con este simulacro, esto estaba causando que fallara una de mis pruebas. La solución simple para mí fue regresar store[key] || null
a la getItem
funció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.js
y 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
localStorage
que usa en su código. (si usa create-react-app
y 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 undefined
valores (no tiene toString()
) y devuelve null
si el valor no existe. react
Probé esto con v15, redux
yredux-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');
window
ni están Storage
definidas. 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 localStorage
en 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 global
agrégalo al objeto
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.
value + ''
en el setter para manejar correctamente los valores nulos e indefinidos