testing if function was called inside vue sfc - vue.js

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?

Related

Vitest & Vue-Test-Utils Testing Function

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.

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 :)

Wait for onMounted() to finish inside test

In my setup I perform a couple of functions inside the onMounted function. In my test I want to wait for these to finish. How can I achieve that?
I tried using the nextTick and the flushPromises(even though there aren't any promises) but none of them work.
Here is some example code:
Vue Component:
setup() {
const mounted = ref(false);
onMounted(() => {
mounted.value = true;
})
}
Test:
describe('TestComponent', () => {
const wrapper = shallowMount(TestComponent)
it('expects mounted to be true after mount', () => {
expect(wrapper.vm.mounted).toBe(true)
})
})
What is missing is exporting the data once the component has been setup:
setup() {
const mounted = ref(false);
onMounted(() => {
mounted.value = true;
})
return { mounted };
}

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()
})

writing jest test cases for util classes

in Vue , I have a util class where i abstracted the axios invocation and bit of logic..
import Axios from 'axios'..the code almost looks like this
export default {
getStudentNumber (name) {
Axios.post('myurl', { studentName: name }).then({
//some logic
//return
})
}
}
This is being invoked from my Vue class... I wrote jest test cases for Vue and I mocked the Axios in that...But is there a way to write separate test cases for this service class ? How to write it? Becuase I have a lot of logic in this... I am using jest
You can write the test for your service like this:
import Axios from 'axios';
import myService from './myService';
jest.mock('axios');
describe('my service', () => {
describe('getStudentNumber', () => {
it('should call Axios.post with myurl and studentName', () => {
myService.getStudentNumber('mock name');
expect(Axios.post).toHaveBeenCalledWith('myurl', { studentName: 'mock name' })
});
describe('given a successful response', () => {
beforeAll(() => {
// setup mock for successful flow
Axios.post.mockResolvedValue({ responseMock });
});
it('should do this', () => {
const result = myService.getStudentNumber();
// describe the expected result
expect(result).toEqual({ ...... });
});
});
describe('given an error', () => {
beforeAll(() => {
// setup mock for non-successful flow
Axios.post.mockRejectedValue(new Error('some mock error'));
});
it('should do that', () => {
const result = myService.getStudentNumber();
// describe the expected result
expect(result).toEqual({ ...... });
});
});
});
});