在Jest测试中,我一直得到“localStorage is not defined”,这是有意义的,但我的选项是什么?碰壁。


当前回答

你可以使用这种方法来避免嘲笑。

Storage.prototype.getItem = jest.fn(() => expectedPayload);

其他回答

在这里引用了一些其他的答案来解决一个用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类,我使用它来访问应用程序中的所有本地存储,而不是直接访问全局本地存储变量。便于在包装器中为测试设置模拟。

至少到目前为止,localStorage可以很容易地在你的笑话测试中被监视,例如:

const spyRemoveItem = jest.spyOn(window.localStorage, 'removeItem')

就是这样。你可以像以前一样使用你的间谍。

一个更好的替代方法,它处理未定义的值(它不需要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

正如Niket Pathak在评论中提到的, 从jest@24 / jsdom@11.12.0及以上开始,localStorage将自动被模拟。

一个使用TypeScript和Jest的更优雅的解决方案。

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