Reputation: 957
I've looked at other answers with this problem, and it seems to be caused by trying to import vue-router
into the test. This however, is not the case for my problem. Here is my test code:
import { mount, shallowMount, createLocalVue } from '@vue/test-utils'
import ListDetails from '../components/homepage/ListDetails'
import EntityList from '../components/homepage/EntityList'
import BootstrapVue from 'bootstrap-vue'
import Vuex from 'vuex'
import faker from 'faker'
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(BootstrapVue)
describe('ListDetails.vue', () => {
it('gets the correct page list when router list id param present', () => {
const selected_list = {
id: faker.random.number(),
name: faker.lorem.words(),
entries: []
}
testRouteListIdParam(selected_list)
})
})
Then in testRouteListIdParam
, I have:
function testRouteListIdParam(selected_list) {
// just assume this sets up a mocked store properly.
const store = setUpStore(selected_list, true)
const $route = {
path: `/homepage/lists/${selected_list.id}`
}
const wrapper = mount(ListDetails, {
mocks: {
$route
},
store,
localVue
})
}
As soon as mount()
happens, I get the error:
[vue-test-utils]: could not overwrite property $route, this is usually caused by a plugin that has added the property as a read-only value
Any ideas why this would be happening? Again, I'm not using VueRouter anywhere in the unit tests, so I'm not sure why I'm getting the error. Could it be BootstrapVue or Vuex that are messing things up?
Upvotes: 16
Views: 12659
Reputation: 513
As I got this error with $bvModal from bootstrap-vue and came across this page, I will post my solution here which might be helpful for anyone getting this error for read-only properties.
First of all, the reason as it is mentioned in other answers is that you register a component or plugin which has a read-only property, therefore you cannot mock it anymore. So the problem is only mocking it not the code itself and I'd rather not change the code.
I removed it from mocks and mocked it after mounting.
in my case we had this:
mockOptions = {
propsData: mockProps,
mocks: {
$store: store,
$route,
$bvModal: {
show: jest.fn(),
hide: jest.fn(),
},
},
localVue,
};
const wrapper = mount(ActionModals, mockOptions);
expect(mockOptions.mocks.$bvModal.hide).toHaveBeenCalledWith('editItemModal');
I changed it to this:
mockOptions = {
propsData: mockProps,
mocks: {
$store: store,
$route,
},
localVue,
};
const wrapper = mount(ActionModals, mockOptions);
jest.spyOn(wrapper.vm.$bvModal, 'hide').mockImplementation(jest.fn());
expect(wrapper.vm.$bvModal.hide).toHaveBeenCalledWith('editItemModal');
Therefore for the route, if you have to register it, mock it after mounting.
Upvotes: 0
Reputation: 18523
janedoe's answer can work but it's often risky to modify production code just to make some tests pass. I prefer to bypass the bug by doing this:
Run your test in watch mode
npx jest --watch src/components/Foo.test.ts
Locate Vue.use(VueRouter)
in your code and diagnose what is the chain of components indirectly running the code by adding this just above
const error = new Error();
console.log(
error.stack
?.split('\n')
.filter((line) => line.includes('.vue'))
.join('\n'),
);
This logs a list of file path like
console.log
at Object.<anonymous> (/path/to/components/Bar.vue:1:1)
at Object.<anonymous> (/path/to/components/Foo.vue:1:1)
Chose a component in this list and, in your test file, mock it
jest.mock('/path/to/components/Bar.vue');
Upvotes: 1
Reputation: 11
I encountered this, and it was because I was importing vueRouter
into a controller, outside of a vueComponent, where this.$router
wasn't defined.
import router from '@/router';
router.push('/foo')
Upvotes: 1
Reputation: 138526
I think these docs are relevant to your situation:
Common gotchas
Installing Vue Router adds
$route
and$router
as read-only properties on Vue prototype.This means any future tests that try to mock
$route
or$router
will fail.To avoid this, never install Vue Router globally when you're running tests; use a
localVue
as detailed above.
The error you're seeing indicates that one of your components (and outside your test code) is installing VueRouter (e.g., Vue.use(VueRouter)
).
To address the issue, search for the VueRouter installation in your component code path, including any of their imports, and refactor it so that the import is not required there. Typically, the installation of VueRouter exists only in main.js
or its imports.
Upvotes: 13
Reputation: 957
So this is a bug with vue-test-utils. If you are using VueRouter anywhere (even if it's not used in any unit test), you will get the above error.
A work around is to use process.env.NODE_ENV in your unit tests and set it to 'test', and wherever you're using VueRouter, check process.env.NODE_ENV like so:
if (!process || process.env.NODE_ENV !== 'test') {
Vue.use(VueRouter)
}
at least until vue-test-utils bug is fixed, this should fix this problem
Upvotes: 19