Jest: 25.1.0 issue with window.location.reload giving '...Cannot assign to read only property...'

Created on 27 Jan 2020  ·  10Comments  ·  Source: facebook/jest

🐛 Bug Report

TypeError: Cannot assign to read only property 'reload' of object '[object Location]'

Reloading a page using Jest mocking is causing an issue with jest version 25.1.0

To Reproduce

import axios from 'axios'
jest.mock('axios')

const location_reload = window.location.reload
beforeAll(() => {
  Object.defineProperty(window.location, 'reload', { configurable: true })
  window.location.reload = jest.fn()
})
afterAll(() => { window.location.reload = location_reload })

beforeEach(() => jest.resetAllMocks() )

Bug Report Needs Repro Needs Triage

Most helpful comment

What worked for me is:

Object.defineProperty(window, 'location', {
  writable: true,
  value: { assign: jest.fn() }
})

Object.defineProperty(window.location, 'reload', {
  writable: true,
  value: { assign: jest.fn() }
})

All 10 comments

Note that location.reload is not the only property affected. location.href, location.assign… are all breaking with the latest jest-environment-jsdom update.

This matches browser behavior. You can try opening up an issue with JSDOM, but I doubt they'll diverge from browser behavior.

I recommend instead of using the browser global you create some sort of abstraction.

export function reload() {
  window.location.reload()
}

Then you can mock that module and replace reload with whatever you want.


Closing as this is working as intended and I don't think we can do anything about it in Jest. Feel free to continue discussing, though!

Courtesy of my colleague @jchiam, this allows me to mock window.location.assign:

    Object.defineProperty(window, 'location', {
      writable: true,
      value: { assign: jest.fn() }
    });

It might work for the other properties in window.location, too.

What worked for me is:

Object.defineProperty(window, 'location', {
  writable: true,
  value: { assign: jest.fn() }
})

Object.defineProperty(window.location, 'reload', {
  writable: true,
  value: { assign: jest.fn() }
})

I don’t understand why restoring the old function. I always do :

// GIVEN a mocked location
delete window.location
window.location = {
      reload: jest.fn()
}
// WHEN the method is called
method()
// THEN the page should have been reload
expect(window.location.reload).toHaveBeenCalledTimes(1)

In test environment, you don’t need to restore original window methods behaviour.

When trying this:

Object.defineProperty(window.location, 'reload', {
  writable: true,
  value: { assign: jest.fn() }
})

I received the error:

TypeError: Cannot redefine property: reload
at Function.defineProperty ()

What worked for me is:

// location.assign
Object.defineProperty(window.location, 'assign', {
  value: jest.fn()
})
expect(window.location.assign).toHaveBeenCalledWith('/path/to/url')

// location.reload
Object.defineProperty(window, 'location', {
  value: { reload: jest.fn() }
})
expect(window.location.reload).toHaveBeenCalled()

Maybe helpful to some. The above examples will work if you execute them in a setupFilesAfterEnv script defined in .jestrc.

json { .... "setupFilesAfterEnv": [ "<rootDir>/config/jest/setupAfterEnv.js" ], .... }

I don’t understand why restoring the old function. I always do :

// GIVEN a mocked location
delete window.location
window.location = {
      reload: jest.fn()
}
// WHEN the method is called
method()
// THEN the page should have been reload
expect(window.location.reload).toHaveBeenCalledTimes(1)

In test environment, you don’t need to restore original window methods behaviour.

You have to restore it if a possible next test uses the variable, otherwise, the mocked value could bias on the result.

Just fyi for anyone landing here after google, using object assign stopped working, but this does (and it's a much simpler way of expressing it)

window.location = {
    ...window.location,
    reload: jest.fn()
}
Was this page helpful?
0 / 5 - 0 ratings