Jestjs 如何在jest测试中处理localStorage?

Jestjs 如何在jest测试中处理localStorage?,jestjs,Jestjs,我在Jest测试中不断得到“localStorage未定义”,这很有意义,但我的选择是什么?撞到砖墙。在以下帮助下找到了答案: 设置包含以下内容的文件: let localStorageMock = (function() { let store = new Map() return { getItem(key: string):string { return store.get(key); }, setItem: function(key: st

我在Jest测试中不断得到“localStorage未定义”,这很有意义,但我的选择是什么?撞到砖墙。

在以下帮助下找到了答案:

设置包含以下内容的文件:

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 });
var localStorageMock=(函数(){
var-store={};
返回{
getItem:函数(键){
返回存储[键];
},
setItem:函数(键、值){
store[key]=value.toString();
},
清除:函数(){
存储={};
},
removeItem:功能(键){
删除存储[密钥];
}
};
})();
defineProperty(窗口'localStorage',{value:localStorageMock});
然后将以下行添加到Jest配置下的package.json中

“SetupTestFrameworkScript文件”:“指向您的文件的路径”,

来自

然而,我们使用ES2015语法,我觉得这样写比较干净

类LocalStorageMock{
构造函数(){
this.store={};
}
清除(){
this.store={};
}
getItem(键){
返回此。存储[键]| |空;
}
setItem(键、值){
this.store[key]=字符串(值);
}
removeItem(键){
删除此项。存储[密钥];
}
};
global.localStorage=新的LocalStorageMock;

一个更好的选择,它处理
未定义的
值(它没有
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

或者你只需要像这样的模拟包:

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]
    }
}


它不仅处理存储功能,还允许您测试是否实际调用了该存储。

在这里提取一些其他答案,以解决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类,用于访问应用程序中的本地存储,而不是直接访问全局本地存储变量。使在包装器中为测试设置模拟变得容易。

如果使用createreact应用程序,则在中介绍了一种简单明了的解决方案

创建
src/setupTests.js
并将其放入其中:

const localStorageMock = {
  getItem: jest.fn(),
  setItem: jest.fn(),
  clear: jest.fn()
};
global.localStorage = localStorageMock;
Tom Mertz在以下评论中的贡献:


然后,您可以通过执行以下操作来测试localStorageMock的函数是否被使用

expect(localStorage.getItem).toBeCalledWith('token')
// or
expect(localStorage.getItem.mock.calls.length).toBe(1)
如果你想确保它被调用的话。正如@ck4建议的那样,检查一下,它清楚地解释了如何开玩笑地使用
localStorage
。但是,模拟函数未能执行任何
localStorage
方法

下面是我的react组件的详细示例,它使用抽象方法来编写和读取数据

//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 };
错误:

修复:
为jest添加以下模拟函数(路径:
.jest/mocks/setUpStore.js

代码段引用自当前(19年10月)的

本地存储不能像您通常所做的那样被jest模仿或监视,正如创建react应用程序文档中所述。这是由于jsdom中所做的更改。您可以在问题和跟踪程序中阅读相关内容

作为一种解决方法,您可以监视原型:

//不起作用:
jest.spyOn(localStorage,setItem);
localStorage.setItem=jest.fn();
//作品:
jest.spyOn(window.localStorage;
window.localStorage.\uuuu proto\uuuuu.setItem=jest.fn();
//一如既往的断言:
expect(localStorage.setItem).toHaveBeenCalled();

如果您正在寻找模拟而不是存根,以下是我使用的解决方案:

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
我导出存储项以便于初始化。也就是说,我可以轻松地将其设置为对象

在Jest+JSDom的较新版本中,无法设置此选项,但localstorage已经可用,您可以像这样监视它:

const setItemSpy = jest.spyOn(Object.getPrototypeOf(window.localStorage), 'setItem');

以下解决方案与更严格的TypeScript、ESLint、TSLint和更漂亮的配置测试兼容:
{“proseWrap”:“始终”、“半”:false、“单引号”: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/了解如何更新global.localStorage

    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
objectt

您可以在setupTests中插入此代码,它应该可以正常工作


我在一个项目中使用typesctipt对其进行了测试。

要在Typescript中执行相同的操作,请执行以下操作:

设置包含以下内容的文件:

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 });
然后将以下行添加到Jest配置下的package.json中

“SetupTestFrameworkScript文件”:“指向您的文件的路径”,


或者,您可以在测试用例中导入此文件,以便模拟本地存储。

您可以使用此方法,以避免模拟

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"]

请随便问任何问题。

不幸的是,我在这里找到的解决方案并不适合我

所以我在研究Jest GitHub的问题时发现了这一点

投票最多的解决方案是:

constspy=jest.spyOn(Storage.prototype,'setItem');
//或
Storage.prototype.getItem=jest.fn(()=>bla');
这对我很有效

delete global.localStorage;
global.localStorage = {
getItem: () => 
 }

我想我会在TypeScript w/React中添加另一个非常适合我的解决方案:

我创建了一个
mockLocalStorage.ts

export const mockLocalStorage=()=>{
const setItemMock=jest.fn();
const getItemMock=jest.fn();
在每个之前(()=>{
Storage.prototype.setItem=setItemMock;
Storage.prototype.getItem=getItemMock;
});
之后(()=>{
setItemMock.mockRestore();
getItemMock.mockRestore();
});
返回{setItemMock,getItemMock};
};
我的组成部分:

export const Component=()=>{
const foo=localStorage.getItem('foo')
返回{foo}
}
然后在我的测试中,我这样使用它:

const setItemSpy = jest.spyOn(Object.getPrototypeOf(window.localStorage), 'setItem');
从“React”导入React;
重要的
interface Spies {
  [key: string]: jest.SpyInstance
}

describe('→ Local storage', () => {

  const spies: Spies = {}

  beforeEach(() => {
    ['setItem', 'getItem', 'clear'].forEach((fn: string) => {
      const mock = jest.fn(localStorage[fn])
      spies[fn] = jest.spyOn(Storage.prototype, fn).mockImplementation(mock)
    })
  })

  afterEach(() => {
    Object.keys(spies).forEach((key: string) => spies[key].mockRestore())
  })

  test('→ setItem ...', async () => {
      localStorage.setItem( 'foo', 'bar' )
      expect(localStorage.getItem('foo')).toEqual('bar')
      expect(spies.setItem).toHaveBeenCalledTimes(1)
  })
})