Vitest & Vue-Test-Utils Testing Function - vue.js

I want to test that the function is called in a simple project.
For this, I read the documentation of Vitest - Function and tried to write test as below.
However, I keep getting an error that the function was not called.
Example.vue
<script setup lang="ts">
const example = () => console.log("TEST");
</script>
<template>
<div>
<button data-testid="button-example" #click="example">Click</button>
</div>
</template>
example.spec.ts
import { describe, vi, it, beforeEach, expect } from "vitest";
import { mount, VueWrapper } from "#vue/test-utils";
import Example from "../src/components/Example.vue";
describe("example testing", () => {
let wrapper: VueWrapper<Example & { [key: string]: any }>;
beforeEach(() => {
wrapper = mount(Example);
});
it("component mounted", () => {
expect(wrapper.exists()).toBeTruthy();
});
it("example function should be called", async () => {
const example = vi.spyOn(wrapper.vm, "example");
await wrapper.find('[data-testid="button-example"]').trigger("click");
expect(example).toHaveBeenCalled();
});
});
And I get the following error.
FAIL test/example.spec.ts > example testing > example function should be called
AssertionError: expected "example" to be called at least once
❯ test/example.spec.ts:21:21
19| await wrapper.find('[data-testid="button-example"]').trigger("click");
20|
21| expect(example).toHaveBeenCalled();
| ^
22| });
23| });
Methods I have tried.
it("example function should be called", async () => {
await wrapper.find('[data-testid="button-example"]').trigger("click");
const example = vi.spyOn(wrapper.vm, "example");
expect(example).toHaveBeenCalled();
});
it("example function should be called", async () => {
await wrapper.find('[data-testid="button-example"]').trigger("click");
expect(wrapper.vm.example).toHaveBeenCalled();
});
But when I do as above "This function is not spy or call spy!" I get an error.
I think I need to test this function because it looks untested in the coverage report.

Related

testing if function was called inside vue sfc

I have the following files:
Component.vue
<template>
...
</template>
<script setup lang=ts>
...
const model = reactive([]);
watch(model, () => {
foo();
});
const foo = () => {
// do something
};
</script>
Component.spec.ts
describe("some test suite", () => {
it("calls 'foo' when model changes", async () => {
const wrapper = mount(Component);
const spyFoo = jest.spyOn(wrapper.vm, "foo");
wrapper.vm.model.push("bar");
expect(spyFoo).toHaveBeenCalledTimes(1);
});
});
What I want to test with this is, if foo() is called when i change the model. But when I do it like this I get the following error:
TypeError: object.hasOwnProperty is not a function
What is the proper way to implement test cases like that?

TypeError: Cannot read property 'type' of null - testing vue component with async functions

I am testing a component ComponentA.spec.js but I am getting TypeError: Cannot read property 'type' of null. It works if I get rid of the await keyword in the getData() function in the ComponentA. I am mocking the getData api call in my test but still it doesn't work.
This is the full stack
TypeError: C:[Privacy]\unknown: Cannot read property 'type' of null
at assert (node_modules/#babel/types/lib/asserts/generated/index.js:284:112)
at Object.assertIdentifier (node_modules/#babel/types/lib/asserts/generated/index.js:373:3)
at new CatchEntry (node_modules/regenerator-transform/lib/leap.js:93:5)
at Emitter.Ep.explodeStatement (node_modules/regenerator-transform/lib/emit.js:535:36)
at node_modules/regenerator-transform/lib/emit.js:323:12
at Array.forEach (<anonymous>)
at Emitter.Ep.explodeStatement (node_modules/regenerator-transform/lib/emit.js:322:22)
at Emitter.Ep.explode (node_modules/regenerator-transform/lib/emit.js:280:40)
This is Component A that i am trying to create tests for
<template>
<div class="d-flex flex-row">
<component-b />
<component-c />
</div>
</template>
<script>
import ComponentB from './ComponentB';
import ComponentC from './ComponentC';
import { getData } from 'apis';
export default {
name: 'component-a',
components: {
ComponentB,
ComponentC,
},
async created() {
await this.getData();
},
methods: {
// This function is the culprit
async getData() {
try {
const response = await getData();
} catch {
//
}
},
},
};
</script>
This is my ComponentA.spec.js file
import Vuetify from 'vuetify';
import ComponentA from 'components/ComponentA';
import { createLocalVue, shallowMount, mount } from '#vue/test-utils';
jest.mock('shared/apis', () => {
const data = require('../fixedData/data.json');
return {
getData: jest.fn().mockResolvedValue(data),
};
});
const localVue = createLocalVue();
let vuetify;
function createShallowWrapper(options = {}) {
return shallowMount(ComponentA, {
localVue,
vuetify,
...options,
});
}
beforeEach(() => {
vuetify = new Vuetify();
});
describe('ComponentA', () => {
describe('component creation', () => {
test('testing', () => {
const wrapper = createShallowWrapper();
expect(wrapper).toMatchSnapshot();
});
});
});
Adding exception variable (e) to my catch in the getData function in ComponentA fixed it.

Vuex + Jest + Composition API: How to check if an action has been called

I am working on a project built on Vue3 and composition API and writing test cases.
The component I want to test is like below.
Home.vue
<template>
<div>
<Child #onChangeValue="onChangeValue" />
</div>
</template>
<script lang="ts>
...
const onChangeValue = (value: string) => {
store.dispatch("changeValueAction", {
value: value,
});
};
</scirpt>
Now I want to test if changeValueAction has been called.
Home.spec.ts
...
import { key, store } from '#/store';
describe("Test Home component", () => {
const wrapper = mount(Home, {
global: {
plugins: [[store, key]],
},
});
it("Test onChangeValue", () => {
const child = wrapper.findComponent(Child);
child.vm.$emit("onChangeValue", "Hello, world");
// I want to check changeValueAction has been called.
expect(wrapper.vm.store.state.moduleA.value).toBe("Hello, world");
});
});
I can confirm the state has actually been updated successfully in the test case above but I am wondering how I can mock action and check if it has been called.
How can I do it?
I have sort of a similar setup.
I don't want to test the actual store just that the method within the component is calling dispatch with a certain value.
This is what I've done.
favorite.spec.ts
import {key} from '#/store';
let storeMock: any;
beforeEach(async () => {
storeMock = createStore({});
});
test(`Should remove favorite`, async () => {
const wrapper = mount(Component, {
propsData: {
item: mockItemObj
},
global: {
plugins: [[storeMock, key]],
}
});
const spyDispatch = jest.spyOn(storeMock, 'dispatch').mockImplementation();
await wrapper.find('.remove-favorite-item').trigger('click');
expect(spyDispatch).toHaveBeenCalledTimes(1);
expect(spyDispatch).toHaveBeenCalledWith("favoritesState/deleteFavorite", favoriteId);
});
This is the Component method:
setup(props) {
const store = useStore();
function removeFavorite() {
store.dispatch("favoritesState/deleteFavorite", favoriteId);
}
return {
removeFavorite
}
}
Hope this will help you further :)

Jest test on functions which are in onMounted Composition API Vue 2

Can anyone tell me how to write a test case for the functions which are in onMounted hook? I am using vue 2 with composition api plugin.
const getUsers = async () => {
const usersQuery = `
query {
users: {
id
username
}
}
`
try {
const result = await apolloClient.getGraphqlData(usersQuery)
if (result) users.value = result.data.users
} catch (err) {
console.log('Error while receiving users', err)
}
}
Below is my onMounted hook
onMounted(() => {
getUsers()
})
You can't mock local functions defined in setup(). The functions have to be exposed so that the unit tests can access them. One workaround is to declare the methods in an external file, adjacent to the component:
// mylib.js
export const getUsers = async () => { /*...*/ }
And import that file in your component:
import { onMounted } from '#vue/composition-api'
import { getUsers } from './mylib'
export default {
setup() {
onMounted(() => getUsers())
}
}
Then in your test file, import the same file, and use jest.mock() to auto-mock it, which would allow you to verify that the function was called upon the component mounting:
import { getUsers } from '#/components/mylib'
import MyComponent from '#/components/MyComponent.vue'
jest.mock('#/components/setupFns')
describe('MyComponent', () => {
beforeEach(() => jest.resetAllMocks())
it('calls getUsers() on mount', () => {
shallowMount(MyComponent)
expect(getUsers).toHaveBeenCalledTimes(1)
})
})
demo

VueJS / Jest : Mocking multiple fetch responses

I have a very simple component relying on two fetch calls :
<template>
<div>
<div ng-if="this.foo">
{{ foo.name }}
</div>
<div ng-if="this.bar">
{{ bar.address }}
</div>
</div>
</template>
<script>
export default {
name: 'identity-card',
data() {
return {
foo:undefined,
bar:undefined
}
}
created() {
Promise.all([
fetch('http://ul/to/api/foo'),
fetch('http://ul/to/api/bar')
]).then(async (response) => {
this.foo = await response[0].json();
this.bar = await response[1].json();
})
}
}
</script>
I'm trying to test that component with Jest.
While I found how to mock a Promise with Jest, I couldn't find a way to mock both fetch responses.
How can I do it without adding an external lib and without potentially refactoring my code?
You could set global.fetch to a jest.fn() that uses the mockReturnValueOnce() API for each expected fetch call:
const makeFetchResp = value => ({ json: async() => value }) // mock fetch().json()
const mockFetch = jest.fn()
.mockReturnValueOnce(makeFetchResp(true)) // 1st fetch() returns true
.mockReturnValueOnce(makeFetchResp(false)) // 2nd fetch() returns false
global.fetch = mockFetch
Before asserting any changes from the fetches, the test needs to flush their Promises for their then callbacks to be invoked. This can be done with:
const flushPromises = () => new Promise(r => setTimeout(r))
await flushPromises()
Your test would look similar to this:
it('fetches foo bar', async () => {
const makeFetchResponse = value => ({ json: async() => value })
const mockFetch = jest.fn()
.mockReturnValueOnce(makeFetchResponse(true))
.mockReturnValueOnce(makeFetchResponse(false))
global.fetch = mockFetch
const wrapper = shallowMount(MyComponent)
await flushPromises()
expect(mockFetch).toHaveBeenCalledTimes(2)
expect(wrapper.vm.foo).toBeTruthy()
expect(wrapper.vm.bar).toBeFalsy()
})