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"]
getItem
difere 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, null
em um navegador, mas undefined
com essa simulação - isso estava causando a falha de um dos meus testes. Solução simples para mim era voltar store[key] || null
na getItem
funçã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.js
e 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
localStorage
você usa no seu código. (se você usa create-react-app
e 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 undefined
valores (não possui toString()
) e retorna null
se o valor não existir. Testei isso com a react
v15 redux
eredux-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');
window
ou Storage
definiram. 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 localStorage
em tom de brincadeira. No entanto, as funções simuladas falharam ao executar qualquer um dos localStorage
mé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 global
objeto
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