Jest: mock window.location methods


There may be a situation where you need to mock methods on window.location in Jest:

it('mocks and calls window.location.reload', () => {
  window.location.reload = jest.fn();
  window.location.reload();
  expect(window.location.reload).toHaveBeenCalled();
});

When you run the test, it fails.

Spy on method

Spying on window.location does make the test pass:

// window.location.reload = jest.fn();
jest.spyOn(window.location, 'reload');

But you’ll get the console error:

console.error node_modules/jsdom/lib/jsdom/virtual-console.js:29
  Error: Not implemented: navigation (except hash changes)

This happens because jsdom is trying to make the method behave like its browser counterpart. However, the test is running in Node.js.

Mock method

To remove the error, location.reload needs to be made configurable before being assigned to a mock:

Object.defineProperty(window.location, 'reload', {
  configurable: true,
});
window.location.reload = jest.fn();

When you run the test this time, it should pass without errors.

Update for jsdom 14+

Unfortunately, this no longer works for jsdom >=14.

The new solution is to delete location and recreate reload as a mock:

delete window.location;
window.location = { reload: jest.fn() };

Credit goes to Josh for bringing this to my attention.

Solution

Here’s the refactored final solution:

describe('window.location', () => {
  const { location } = window;

  beforeAll(() => {
    delete window.location;
    window.location = { reload: jest.fn() };
  });

  afterAll(() => {
    window.location = location;
  });

  it('mocks `reload`', () => {
    expect(jest.isMockFunction(window.location.reload)).toBe(true);
  });

  it('calls `reload`', () => {
    window.location.reload();
    expect(window.location.reload).toHaveBeenCalled();
  });
});

Gist

The gist that inspired this post:

Repl.it

Run the test in this repl.it:



If you enjoyed this post, please consider supporting this site!