如何用Jasmine监视localStorage方法?解决测试报错问题
The error Error: <toHaveBeenCalled> : Expected a spy, but got Function. happens because of how older Firefox versions handle the localStorage object:
- In Firefox 45.9.0, the methods of
localStorage(likegetItem) are non-configurable and non-writable native functions. - Jasmine's
spyOnworks by replacing the target method with a spy function. But if the original method can't be modified (due to non-configurable/writable flags), the spy never actually replaces the nativegetItemmethod. - When your test runs
expect(localStorage.getItem).toHaveBeenCalled(), it's checking the original native function—not the spy you tried to create. Jasmine throws the error because only spies have thetoHaveBeenCalled()matcher.
The most reliable fix is to mock the entire localStorage object instead of trying to spy on the native one. This isolates your test from the real browser storage and avoids issues with non-configurable methods.
Solution: Mock LocalStorage
Here's how to rewrite your test with a mock:
it('should return true when someKey is "true"', function() { // Save the original localStorage to restore later const originalLocalStorage = window.localStorage; // Create a mock localStorage with spy methods const mockLocalStorage = { getItem: jasmine.createSpy('getItem').and.returnValue('true'), removeItem: jasmine.createSpy('removeItem') }; // Replace the global localStorage with our mock window.localStorage = mockLocalStorage; try { const returnValue = modifiesLocalStorage(); // Verify the spies were called correctly expect(mockLocalStorage.getItem).toHaveBeenCalledWith('someKey'); expect(mockLocalStorage.removeItem).toHaveBeenCalledWith('someKey'); expect(returnValue).toBeTruthy(); } finally { // Restore the original localStorage to avoid breaking other tests window.localStorage = originalLocalStorage; } });
Alternative: Make the Method Configurable (Less Preferred)
If you prefer to use the real localStorage (not recommended for test isolation), you can redefine the getItem method to be configurable before spying on it:
it('should return true when someKey is "true"', function() { // Override the property descriptor to make getItem configurable/writable Object.defineProperty(localStorage, 'getItem', { configurable: true, writable: true }); // Now spyOn will work as expected spyOn(localStorage, 'getItem').and.returnValue('true'); spyOn(localStorage, 'removeItem'); const returnValue = modifiesLocalStorage(); expect(localStorage.getItem).toHaveBeenCalledWith('someKey'); expect(localStorage.removeItem).toHaveBeenCalledWith('someKey'); expect(returnValue).toBeTruthy(); });
Note: This approach might have unintended side effects on other tests or the real localStorage, so the mock method is always safer.
内容的提问来源于stack exchange,提问作者Mumzee

