Respostas:
Ótima solução de @chiedo
No entanto, usamos a sintaxe do ES2015 e achei que era um pouco mais limpo escrevê-lo dessa maneira.
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 isso que meu teste estava falhando, porque no meu teste eu estava usando not.toBeDefined(). @Chiedo solução fazê-lo funcionar novamente
Descobri isso com a ajuda do seguinte: https://groups.google.com/forum/#!topic/jestjs/9EPhuNWVYTg
Configure um arquivo com o seguinte conteúdo:
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 });
Então você adiciona a seguinte linha ao seu package.json nas configurações do Jest
"setupTestFrameworkScriptFile":"PATH_TO_YOUR_FILE",
"setupFiles": [...]funciona também. Com a opção de matriz, permite separar simulações em arquivos separados. Por exemplo:"setupFiles": ["<rootDir>/__mocks__/localStorageMock.js"]
getItemdifere um pouco do que seria retornado por um navegador se nenhum dado for definido em uma chave específica. chamar getItem("foo")quando não está definido retornará, por exemplo, nullem um navegador, mas undefinedcom essa simulação - isso estava causando a falha de um dos meus testes. Solução simples para mim era voltar store[key] || nullna getItemfunção
localStorage['test'] = '123'; localStorage.getItem('test')
Se estiver usando create-react-app, há uma solução mais simples e direta explicada na documentação .
Crie src/setupTests.jse coloque isso nele:
const localStorageMock = {
getItem: jest.fn(),
setItem: jest.fn(),
clear: jest.fn()
};
global.localStorage = localStorageMock;
Tom Mertz contribuição em um comentário abaixo:
Em seguida, você pode testar se as funções do seu localStorageMock são usadas fazendo algo como
expect(localStorage.getItem).toBeCalledWith('token')
// or
expect(localStorage.getItem.mock.calls.length).toBe(1)
dentro de seus testes, se você quiser ter certeza de que foi chamado. Confira https://facebook.github.io/jest/docs/en/mock-functions.html
localStoragevocê usa no seu código. (se você usa create-react-appe todos os scripts automáticos que fornece naturalmente)
expect(localStorage.getItem).toBeCalledWith('token')ou expect(localStorage.getItem.mock.calls.length).toBe(1)dentro dos testes, se você quiser ter certeza de que foi chamado. Confira facebook.github.io/jest/docs/en/mock-functions.html
localStorage? Você não gostaria de redefinir os espiões após cada teste para evitar a "propagação" em outros testes?
Atualmente (outubro de 19), o localStorage não pode ser ridicularizado ou espionado pelo gracejo, como você faria normalmente, e conforme descrito nos documentos de criação e reação de aplicativos. Isso ocorre devido a alterações feitas no jsdom. Você pode ler sobre isso nos rastreadores de brincadeiras e jsdom .
Como solução alternativa, você pode espionar o protótipo:
// 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');
ou você apenas pega um pacote simulado como este:
https://www.npmjs.com/package/jest-localstorage-mock
Ele lida com não apenas a funcionalidade de armazenamento, mas também permite testar se a loja foi realmente chamada.
Uma alternativa melhor que lida com undefinedvalores (não possui toString()) e retorna nullse o valor não existir. Testei isso com a reactv15 reduxeredux-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/pt-BR/docs/Web/API/Storage/removeItem
Se você está procurando um mock e não um stub, aqui está a solução que eu 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 os itens de armazenamento para facilitar a inicialização. IE eu posso facilmente configurá-lo para um objeto
Nas versões mais recentes do Jest + JSDom, não é possível definir isso, mas o armazenamento local já está disponível e você pode espioná-lo da seguinte forma:
const setItemSpy = jest.spyOn(Object.getPrototypeOf(window.localStorage), 'setItem');
Encontrei esta solução no 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
});
Você pode inserir esse código em seus setupTests e deve funcionar bem.
Eu testei em um projeto com tip typtipt.
Infelizmente, as soluções que encontrei aqui não funcionaram para mim.
Então, eu estava olhando para os problemas do Jest GitHub e encontrei este tópico
As soluções mais votadas foram as seguintes:
const spy = jest.spyOn(Storage.prototype, 'setItem');
// or
Storage.prototype.getItem = jest.fn(() => 'bla');
windowou Storagedefiniram. Talvez seja a versão mais antiga do Jest que estou usando.
Como @ ck4, a documentação sugerida tem uma explicação clara para o uso localStorageem tom de brincadeira. No entanto, as funções simuladas falharam ao executar qualquer um dos localStoragemétodos.
Abaixo está o exemplo detalhado do meu componente react, que faz uso de métodos abstratos para escrever e ler dados,
//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 };
Erro:
TypeError: _setupLocalStorage2.default.setItem is not a function
Fix:
Adicionar abaixo função simulada para jest (caminho: .jest/mocks/setUpStore.js)
let mockStorage = {};
module.exports = window.localStorage = {
setItem: (key, val) => Object.assign(mockStorage, {[key]: val}),
getItem: (key) => mockStorage[key],
clear: () => mockStorage = {}
};
O snippet é referenciado a partir daqui
Apresentamos algumas outras respostas aqui para resolvê-lo em um projeto com o Typecript. Eu criei um LocalStorageMock assim:
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]
}
}
Em seguida, criei uma classe LocalStorageWrapper que eu uso para todo o acesso ao armazenamento local no aplicativo em vez de acessar diretamente a variável de armazenamento local global. Facilitou a configuração da simulação no wrapper para testes.
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);
});
});
Crie uma simulação e adicione-a ao globalobjeto
Você precisa simular o armazenamento local com esses trechos
// 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
});
E na configuração de brincadeira:
"setupFiles":["localStorage.js"]
Sinta-se livre para perguntar qualquer coisa.
A solução a seguir é compatível para testes com configurações mais rigorosas do TypeScript, ESLint, TSLint e 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 para saber como atualizar global.localStorage
Para fazer o mesmo no texto datilografado, faça o seguinte:
Configure um arquivo com o seguinte conteúdo:
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 });
Então você adiciona a seguinte linha ao seu package.json nas configurações do Jest
"setupTestFrameworkScriptFile":"PATH_TO_YOUR_FILE",
Ou você importa esse arquivo no seu caso de teste em que deseja simular o armazenamento local.
value + ''na setter como nulo alça e valores indefinidos corretamente