Vue unit test is holds old component code in wrapper - vue.js

I am doing unit test of vue component methods through vue-test-utils and facing an weird issue. wrapper.vm.somemthod() is executing old code that was written earlier inside the method. It's printing old console.log statements. If I put new console.log statement, it's not printing that at all. Am I missing something?
import { mount, shallowMount } from '#vue/test-utils'
import TestComponent from '#/components/TestComponent.vue'
const mockMixin = {
methods: {
InnerMethod() {
return 2;
},
}
}
describe('Test Screen', () => {
let wrapper;
let mock;
beforeAll(() => {
mock = new MockAdapter(api);
})
beforeEach(() => {
jest.resetModules();
wrapper = mount(TestComponent, {
i18n,
vuetify,
mixins: [mockMixin],
data() {
},
mocks: {
$t: (msg) => msg,
$config: (value) => value,
$store: (value) => value,
$route: (value) => value,
}
});
})
afterEach(() => {
mock.reset();
wrapper.destroy();
wrapper = null;
});
describe('Component Test', () => {
it('getdata', async () => {
expect(wrapper.vm.somedata.length).toBe(0);
const spy = jest.spyOn(wrapper.vm, 'InnerMethod');
wrapper.vm.someMethod();
expect(spy).toBeCalledTimes(1);
expect(wrapper.vm.somedata.length).toBe(2);
});
});
});

Related

Jest: How I should change the mock data of Vuex in each test?

I've been working in a test where I need the data from Vuex. However, I'm having some problems, I need to change that data in each test in order to test the functionality of the component.
Here is my component:
<template>
<div id="cb-items-displayer" #click="textClick">
<span>(</span>
<p>{{ text }}</p>
<span>)</span>
</div>
</template>
<script lang="ts" setup>
import { capitalize } from '#/utils/capitalize'
import { ItemsDisplayer } from '#/models/ItemsDisplayer'
import { computed, PropType } from 'vue'
import { useStore } from 'vuex'
const store = useStore()
const props = defineProps({
type: {
type: String,
default: '',
},
menuType: {
type: String,
default: '',
},
items: {
type: Array as PropType<ItemsDisplayer[]>,
default: () => [],
}
})
const emit = defineEmits<{
(event: 'textClicked'): void
}>()
const text = computed(() => {
const param = props.menuType === 'radio' ? 'One' : 'Many'
console.log( "TYPEEE ", props.type, " ", param )
const itemsIds = store.getters['filters/get' + capitalize(props.type) + param]
console.log("ITEMSSS", JSON.stringify(itemsIds))
return getTextToShow(itemsIds)
})
const getTextToShow = (itemsIds: string) => {
//TODO - improve it
if (itemsIds === 'all') {
return 'all'
} else if (itemsIds.length === 0) {
return '-'
} else if (itemsIds.length === 1) {
return getName(itemsIds[0], props.items)
} else {
return itemsIds.length
}
}
const textClick = () => {
emit('textClicked')
}
const getName = (id: string, items: ItemsDisplayer[]) => {
const found: ItemsDisplayer = items.find((x) => x.id! === id) as ItemsDisplayer
console.log("GETNAME ", found.name)
return found?.name
}
</script>
And this is the test:
import { render, screen, click, waitFor } from '#tests/app-test-utils'
import ItemsDisplayer from './ItemsDisplayer.vue'
import { capitalize } from '#/utils/capitalize'
let mockStoreCommit: jest.Mock
jest.mock('vuex', () => ({
...jest.requireActual('vuex'),
useStore: () => ({
getters: {
[`filters/get${capitalize('categories')}Many`]: [],
},
commit: mockStoreCommit,
}),
}))
describe('ItemsDisplayer', () => {
beforeEach(() => {
mockStoreCommit = jest.fn()
render(
ItemsDisplayer,
{},
{
props: {
type: 'categories',
menuType: 'checkbox',
items: [
{
box_templates:"",
id:"1",
name:"Culture"
},
{
box_templates:"",
id:"2",
name:"Economy"
},
{
box_templates:"",
id:"3",
name:"Education"
}
]},
}
)
})
it('renders the component', async() => {
await screen.getByText('-')
})
it('renders the component with one item', async() => {
//DON'T WORK HERE THERE SHOULD BE A CHANGE OF DATA IN THE MOCKED STORE IN ORDER TO WORK
await screen.getByText('Culture')
})
})
My problem is that I need to change the value of [filters/get${capitalize('categories')}Many] to ["1"] in the second test.
I tried several things in order to change the mocked data but they don't work. How can I change the mocked store data in each test?
Thanks!
You can achieve this by lazy loading your vue component:
Add jest.resetModules(); in the beforeEach to reset all of the imported modules before each test so they can be re-evaluated and re-mocked:
beforeEach(() => {
jest.resetModules();
In each unit test, you will first need to import the vue component using the require syntax as follows:
const ItemsDisplayer = require('./ItemsDisplayer.vue').default;
Then add the mock directly after the import with the [`filters/get${capitalize('categories')}Many`] value being set to whatever you want it to be:
jest.mock('vuex', () => ({
...jest.requireActual('vuex'),
useStore: () => ({
getters: {
[`filters/get${capitalize('categories')}Many`]: ["1"],
},
commit: mockStoreCommit,
}),
}));
I have noticed that you do your rendering in the beforeEach. Unfortunately because you import and mock your modules during the test, the rendering will need to be done after these have taken place - hence you will need to either move that logic into your unit test or extract it into another function which can be called from within the unit test.
Each unit test should look something like this:
it('renders the component', async() => {
const ItemsDisplayer = require('./ItemsDisplayer.vue').default;
jest.mock('vuex', () => ({
...jest.requireActual('vuex'),
useStore: () => ({
getters: {
[`filters/get${capitalize('categories')}Many`]: ["1"],
},
commit: mockStoreCommit,
}),
}));
// beforeEach logic here or a call to a function that contains it
await screen.getByText('-')
})

Timeout simulation not working with testing-library and useFakeTimers

I'm working on a vueJS component that allows to display a modal after 5 seconds. the component works well as expected.
<template>
<vue-modal v-if="showModal" data-testid="modal-testid" />
</template>
<script>
export default {
name: "TimeoutExample",
data() {
return {
showModal: false,
}
},
mounted() {
setTimeout(() => this.displayModal(), 5000)
},
methods: {
displayModal: function() {
this.showModal = true;
}
}
};
</script>
I implemented the unit tests using jest, testing-library and I wanted to use jest.useFakeTimers to simulate the timeout, but the test is KO.
// testing file
describe.only('Vue Component (mobile) 2', () => {
beforeAll(() => {
isMobile.mockImplementation(() => true)
})
beforeEach(() => {
jest.useFakeTimers()
})
afterEach(() => {
jest.runOnlyPendingTimers()
jest.useRealTimers()
})
it('should render title after `props.delay` milliseconds', () => {
const { queryByTestId } = myRender({
localVue: myMakeLocalVue(),
})
jest.advanceTimersByTime(5001)
expect(queryByTestId('modal-testid')).toBeVisible()
})
})
do you have any idea how i can test this behavior?
remove this jest.spyOn(global, 'setTimeout'). jest will do it's own magic with for this with useFakeTimers
I suppose you can not use async and done callback in one test case. Which version of jest do you use?
Add await localVue.$nextTick() after advanceTimersByTime to wait until Vue apply all the changes
It works for me after calling advanceTimersByTime inside waitFor.
describe.only('Vue Component (mobile) 2', () => {
beforeAll(() => {
isMobile.mockImplementation(() => true)
})
beforeEach(() => {
jest.useFakeTimers()
})
afterEach(() => {
jest.runOnlyPendingTimers()
jest.useRealTimers()
})
it('should render title after `props.delay` milliseconds', async () => {
const { queryByTestId } = myRender({
localVue: myMakeLocalVue(),
})
await waitFor(() => {
jest.advanceTimersByTime(5001)
})
expect(queryByTestId('modal-testid')).toBeVisible()
})
})

Unit tests, check if function have been called

I need to implement a test that checks if the function has been called on the button click
onSave (e) {
this.$qiwaApi.createDimension()
.then(() => {})
.catch(err => this.showSnackbar(err.message))}
I need to test the function createDimension. In my test i mocked it
const createComponent = () => {
wrapper = mount(dimensions, {
store,
localVue,
mocks: {
$qiwaApi: {
createDimension: function (e) {
return new Promise((resolve) => { resolve({}) })
}
}
},
router
})
}
In the project, the function exported this way
export default $axios => ({
createDimension (data, surveyId) {
return $axios.post(`/lmi-admin/surveys/${surveyId}/dimension`, {
data: {
attributes: {
...data
}
}
})
}
})
I expect this test to work. But for some reason wrapper.qiwaApi or wrapper.createDimension return undefined
expect(wrapper.$qiwaApi.createDimension).toHaveBeenCalled()
The wrapper doesn't provide access to your mocks that way.
You would have to hold a reference to a jest.fn(), and then verify the calls on that reference directly instead of trying to pull it out of the wrapper:
it('calls createDimension on button click', async () => {
const createDimension = jest.fn(() => Promise.resolve())
const wrapper = mount(dimensions, {
mocks: {
$qiwaApi: {
createDimension
}
}
})
await wrapper.find('[data-testid="save"]').trigger('click')
expect(createDimension).toHaveBeenCalled()
})
demo

Vue.js/nuxt.js - test a dynamically added method inside a component

I'm trying to make a test for dynamically created methods in one of my components the code goes like this.
<template>
<div id="app">
<div #click="executeDynamic('myCustomFunction')">Click me!</div>
</div>
</template>
<script>
export default {
name: "App",
data () {
return {
// These contain all dynamic user functions
userFuncs: {}
}
},
created () {
window.setTimeout(() => {
this.$set(this.userFuncs, 'myCustomFunction', () => {
console.log('whoohoo, it was added dynamically')
})
}, 2000)
},
methods: {
executeDynamic (name) {
if (this.userFuncs[name]) {
this.userFuncs[name]()
} else {
console.warn(`${name} was not yet defined!`)
}
}
}
};
</script>
test file
import WorkDateTime from "#/components/WorkDateTime.vue"
import Vue from "vue"
describe("WorkDateTime.vue", () => {
it("allowedDatesFrom: today -> NG", () => {
const that = {
$set: Vue.set
}
expect(WorkDateTime.data.userFuncs['myCustomFunction']).toBeTruthy()
})
}
code pen
https://codesandbox.io/s/vue-template-forked-ec7tg?file=/src/App.vue:0-662
Try something like that:
import { shallowMount } from '#vue/test-utils';
import WorkDateTime from '#/components/WorkDateTime.vue';
describe('WorkDateTime.vue', () => {
it('userFuncs empty', () => {
let wrapper = shallowMount(WorkDateTime);
expect(wrapper.vm.userFuncs).toStrictEqual({});
});
it('userFuncs filled', async () => {
let wrapper = shallowMount(WorkDateTime);
let wait3Seconds = () => new Promise(resolve => setTimeout(() => resolve(), 3000));
await wait3Seconds();
expect(wrapper.vm.userFuncs['myCustomFunction']).toBeInstanceOf(Function);
});
});

How to mock axios call within a method?

I am trying to mock an axios call within a vuejs method. Is this possible?
Here is my vue component (SomeObj):
methods:{
callAxiosMethod() {
const callApi= axios.create();
callApi.defaults.timeout = 10000;
callApi.get(mockedUrl)
.then(response => {
console.log('response is ' + response);
})
.catch(e => {});
}
}
Here is my spec.js
let mockData = {};
beforeEach(() => {
jest.spyOn(axios, 'get').mockReturnValue(Promise.resolve(mockData));
});
let wrapper = shallowMount(SomeObj, {
stubs: [], localVue, mocks: {
mockUrl: mockUrl,
$route: {
params: { testId: "123" }
}
}
});
it('is a Vue instance', () => {
expect(wrapper.isVueInstance()).toBeTruthy();
axios.get.mockResolvedValue(mockData);
wrapper.vm.callAxiosMethod();
})
When I looked at the coverage, the system says the callApi is not covered. Any idea on how I can mock the axios call within the function?
Your code calls axios.create so you need to mock that function to return a mock callApi object.
Here is a simplified working example:
code.js
import * as axios from 'axios';
const mockedUrl = 'http://mock-url';
export const callAxiosMethod = () => {
const callApi = axios.create();
callApi.defaults.timeout = 10000;
return callApi.get(mockedUrl); // <= return the Promise so it can be awaited
}
code.test.js
import { callAxiosMethod } from './code';
jest.mock('axios', () => ({
create: jest.fn().mockReturnValue({
defaults: {},
get: jest.fn().mockResolvedValue('mocked data')
})
}));
test('callAxiosMethod', async () => { // <= async test function
const response = await callAxiosMethod(); // <= await the Promise
expect(response).toBe('mocked data'); // Success!
});