Vue testing with jest: $route is always undefined - vue.js

I've tried a lot of things but $route seems to always be undefined and I have this error on shallowMount: TypeError: Cannot read property 'params' of undefined.
In my component, I want to check the params of the $route but it is always undefined. I've looked the documentation and mocked the $route to set it but it seems like the $route is not mocked. I've also tried using localVue.use(VueRouter), thinking that it would set $route but it didn't. Does anyone have an idea why the following doesn't work in both cases?
my-component.ts
#Component
export default class MyComponent extends Vue {
get organisationId() {
return this.$route.params.organisationId;
}
}
I've tried the 2 following tests with the solutions I've talked about:
my-component.spec.ts
import { createLocalVue, shallowMount } from '#vue/test-utils';
import MyComponent from '#/components/MyComponent.vue';
import router from '#/router';
const localVue = createLocalVue();
describe('MyComponent', () => {
let cmp: any;
beforeEach(() => {
cmp = shallowMount(MyComponent, {
localVue,
router,
mocks: {
$route: {
params: {
id: 'id'
}
}
}
});
});
it('Should render a Vue instance', () => {
expect.assertions(1);
expect(cmp.isVueInstance()).toBeTruthy();
});
});
my-component.spec.ts
import { createLocalVue, shallowMount } from '#vue/test-utils';
import MyComponent from '#/components/MyComponent.vue';
const localVue = createLocalVue();
describe('MyComponent', () => {
let cmp: any;
localVue.use(VueRouter);
const router = new VueRouter();
beforeEach(() => {
cmp = shallowMount(MyComponent, {
localVue,
router
});
});
it('Should render a Vue instance', () => {
expect.assertions(1);
expect(cmp.isVueInstance()).toBeTruthy();
});
});

You were mocking $route right in #1 test. You can access $route property of test component via wrapper.vm.$route (or cmp.vm.$route in your case). And There is example:
import { shallowMount, Wrapper } from '#vue/test-utils'
import MyComponent from '#/components/MyComponent.vue'
let wrapper: Wrapper<MyComponent>
describe('MyComponent', () => {
beforeEach(() => {
wrapper = shallowMount(MyComponent, {
mocks: {
$route: {
params: {
id: 'id'
}
}
}
})
})
it('$route has passed params', () => {
expect(wrapper.vm.$route.params.id).toBe('id')
})
})

Related

Mocking mixin in Vue Test libs

I'm having a couple of issues with my Vue test libs. I am trying to test a mixin. It requires setting the route and mocking a function. Here is my code
MIXIN
export const CampaignNotifier = {
mounted () {
// Create tagname with dynamic currency parameter from banking app
let routeName = this.$route.name
let queryParamCurrency = (this.$route.query.currency) ? `- ${ this.$route.query.currency.toUpperCase() }` : '-'
this.campaignTagName = (BRAZE_TAG_MAPPING[routeName]) ? BRAZE_TAG_MAPPING[routeName].replace(/-/, queryParamCurrency) : null
if (this.campaignTagName) {
this.$appboy.logCustomEvent(this.campaignTagName)
}
},
}
TEST:
import { expect } from 'chai'
import { shallowMount, createLocalVue } from '#vue/test-utils'
import VueRouter from 'vue-router'
import sinon from 'sinon'
import { CampaignNotifier } from '#/mixins/campaignNotifier'
let wrapper
function factory (routeName, currency) {
let localVue = createLocalVue()
localVue.use(VueRouter)
let routes = [
{
path: routeName,
name: routeName,
query: {
currency
}
}
]
let router = new VueRouter({
routes
})
let Component = {
render () { },
mixins: [CampaignNotifier],
localVue,
router,
mocks: {
$route: {
path: routeName,
query: {
currency
}
},
$appboy: {
logCustomEvent: () => {}
}
}
}
return shallowMount(Component)
}
describe('#/mixins/campaignNotifier', () => {
it('Campaign Notifier is not called if route not configured correctly', () => {
wrapper = factory('before-begin', 'EUR')
console.log('$route ***', wrapper.vm.$route)
sinon.spy(wrapper.vm.$appboy, 'logCustomEvent')
expect(wrapper.vm.$appboy.logCustomEvent).not.toHaveBeenCalled()
})
})
Issues I am encountering:
When I mock the $route and console.log it, it returns undefined. I tried mocking it and also using VueRouter. Neither worked.
I am trying to mock the global prototype / method $appboy.logCustomEvent I get:
[Vue warn]: Error in mounted hook: "TypeError: Cannot read property 'logCustomEvent' of undefined"
Any help would be greatly appreciated. Thanks
You're incorrectly combining mounting options with the component definition itself, so your mocks aren't actually getting installed, leading to the errors you observed.
Mounting options should be passed as the second argument to shallowMount:
function factory() {
let Component = {
render() { },
mixins: [CampaignNotifier],
}
let mountingOptions = {
localVue,
router,
mocks: {
$route: {
path: routeName,
query: {
currency
}
},
$appboy: {
logCustomEvent: () => { }
}
}
}
return shallowMount(Component, mountingOptions)
}

Jest : TypeError: Cannot read property 'variable' of undefined

I am testing though Jest on the Vue 2.x, nuxtjs and #nuxtjs/composition-api.
However, the state value in the components has undefined value when testing though jest
List.spec.js
import Vue from 'vue';
import Vuetify from 'vuetify';
import { createLocalVue, shallowMount } from '#vue/test-utils';
import List from '#/components/home/list.vue';
Vue.use(Vuetify);
describe('List.vue', () => {
const localVue = createLocalVue();
let vuetify;
const $t = () => {};
const localePath = () => {};
beforeEach(() => {
vuetify = new Vuetify();
localVue.use(vuetify);
});
const mockOrder = [
{
coardshare: {
cs_id: 123,
},
},
{
talkboard: {
cs_id: 123,
},
},
];
it('11111', () => {
const wrapper = shallowMount(List, {
localVue,
vuetify,
propsData: { data: mockOrder },
mocks: { $t, localePath },
data() {
return {
data: mockOrder,
};
},
});
expect(wrapper.html()).toMatchSnapshot();
const title = wrapper.find('.v-card__title > span');
expect(title.text()).toBe('Foobar');
});
});
List.vue
<template>
...
<div v-for="item in state.data.talkboard" :key="item.cs_id">
<ListItem :item="item"></ListItem>
</div>
...
</template>
<script>
import { reactive, onMounted, useContext } from '#nuxtjs/composition-api';
import axios from 'axios';
import Header from './header';
import ListItem from './list-item.vue';
export default {
name: 'ListHome',
components: {
Header,
ListItem,
},
setup() {
const state = reactive({
data: [],
});
const { store } = useContext();
const fatch = async () => {
....
};
onMounted(fatch);
return {
state,
fatch,
};
},
};
</script>
error message
TypeError: Cannot read property 'data' of undefined
I am testing though Jest on the Vue 2.x, nuxtjs and #nuxtjs/composition-api.
However, the state value in the components has undefined value when testing though jest
why error on this ?? because of composition API that define the state with reactive() function ??
In your test file maybe you can try something like this:
it('11111', () => {
const wrapper = shallowMount(List, {
localVue,
vuetify,
propsData: { data: mockOrder },
mocks: { $t, localePath },
data: () => {
return {
data: mockOrder,
};
},
});

Mock of store action of vuex does not mocked

I have small vue component that on created hook dispatch some action
#Component
export default class SomeComponent extends Vue {
created() {
store.dispatch('module/myAction', { root: true });
}
}
and I wrote next test
const localVue = createLocalVue();
localVue.use(Vuex);
localVue.use(VueRouter);
const localRouter = new VueRouter();
describe('SomeComponent.vue Test', () => {
let store: any;
beforeEach(() => {
store = new Vuex.Store({
modules: {
module: {
namespaced: true,
actions: {
myAction: jest.fn()
}
}
}
});
});
it('is component created', () => {
const wrapper = shallowMount(SomeComponent, {
localVue,
store,
propsData: {}
});
expect(wrapper.isVueInstance()).toBeTruthy();
});
});
but for some reason the "real" code are executed and I got a warning
isVueInstance() is deprecated. In your test you should mock $store object and it's dispatch function. I fixed typo in created(), here's my version of SomeComponent and working test, hope that would help.
#Component
export default class SomeComponent extends Vue {
created () {
this.$store.dispatch('module/myAction', { root: true })
}
}
import { shallowMount, Wrapper } from '#vue/test-utils'
import SomeComponent from '#/components/SomeComponent/SomeComponent.vue'
let wrapper: Wrapper<SomeComponent & { [key: string]: any }>
describe('SomeComponent.vue Test', () => {
beforeEach(() => {
wrapper = shallowMount(SomeComponent, {
mocks: {
$store: {
dispatch: jest.fn()
}
}
})
})
it('is component created', () => {
expect(wrapper.vm.$store.dispatch).toBeCalled()
expect(wrapper.vm.$store.dispatch).toBeCalledWith('module/myAction', { root: true })
})
})
Also keep in mind that when you test SomeComponent or any other component you should not test store functionality, you should just test, that certain actions/mutations were called with certain arguments. The store should be tested separately. Therefore you don't need to create real Vuex Store when you test components, you just need to mock $store object.

$router.push not triggering in unit testing

I have the following problem while trying to unit test my Vue application.
Even spying and mocking $router.push, I still can't make it to be called while inside unit testing:
This is my unit testing (Home.spec.js)
const localVue = createLocalVue();
localVue.use(Vuex);
localVue.use(VueRouter);
describe('Home.vue', () => {
let actions;
let getters;
let store;
let router;
beforeEach(() => {
actions = {
[FETCH_USER_REPOSITORIES]: jest.fn()
};
getters = {
getTopThreeRepositories: jest.fn(repositoryMock.getRepositories)
};
store = new Vuex.Store({ getters, actions });
router = new VueRouter();
});
it('should redirect when 404 status code received', async () => {
jest.spyOn(store, 'dispatch').mockRejectedValue({ statusCode: 404 });
jest.spyOn(router, 'push').mockResolvedValue({});
const wrapper = await shallowMount(Home, {
store,
localVue,
router
});
expect(router.push).toHaveBeenCalledWith('/not-found');
});
});
Now, this is my Home.vue:
import { mapGetters } from 'vuex';
import { FETCH_USER_REPOSITORIES } from "../store/actions";
import RepositoryList from "#/components/RepositoryList";
import Card from "#/components/Card";
export default {
name: 'view-home',
components: {
Card,
RepositoryList
},
async beforeMount() {
try {
await this.$store.dispatch(FETCH_USER_REPOSITORIES, 'some-repo');
} catch(err) {
console.log(this.$router);
await this.$router.push('/not-found');
}
},
computed: {
...mapGetters(["getTopThreeRepositories"])
}
}
The console log shows the $router correctly, with the spy.
If I force the calling inside the unit testing, it works, but the expects always fails giving me back that $router.push hasn't been called.
Can anyone help me, please?
Thanks!
You should specify $store and $route as mocks in your mounting options, as shown below. Also there's no need to await shallowMount because shallowMount does not return a Promise, so the await would just return immediately.
describe('Home.vue', () => {
it('should redirect when 404 status code received', () => {
const $store = {
dispatch: jest.fn()
}
const $router = {
push: jest.fn()
}
const wrapper = shallowMount(Home, {
localVue,
mocks: {
$store,
$router,
}
});
expect($router.push).toHaveBeenCalledWith('/not-found');
})
})

Vue: Test utils vue composition - root.$route

Versions
Test utils
1.0.3
Vue
2.6.11
Vue router
3.20
Vue compsotion API
1.0.0-beta.8
I have some difficulties developing a component test because of root.$route.
In the component, I have a watch, which will be aware when the route changes, in order to change some variables of the component.
My component
<script>
export default {
setup ( _, { root }) {
// behavior
...
watch( () => root.$route,
({ query, meta }) => {
foo = 'bar';
}, {
immediate: true
});
}
};
</script>
My test
import { shallowMount, createLocalVue } from '#vue/test-utils';
import VueCompositionAPI from '#vue/composition-api';
import Component from '#/components/bundles/layout/Component';
import router from '#/router/';
const localVue = createLocalVue();
localVue.use( VueCompositionAPI );
localVue.use( router );
describe( 'Component', () => {
it( 'Insert', () => {
const wrapper = shallowMount( Component, {
mocks: {
$t: () => ({})
},
localVue,
router
});
console.log( wrapper.html() );
});
});
Error
TypeError: Cannot destructure property 'query' of 'undefined' as it is undefined.
I've tried to mock the router, but without effect.
I tried in the following ways:
1:
...
describe( 'Component', () => {
it( 'Insert', () => {
const wrapper = shallowMount( Component, {
mocks: {
$t: () => ({}),
$route: { query: '', meta: '' }
},
localVue,
router
});
console.log( wrapper.html() );
});
});
2:
import router from '#/router/';
jest.mock( '#/router/', () => ({
currentRoute: {
meta: {},
query: {}
},
push: jest.fn( () => Promise.resolve() )
}) );
Some help?