How to unit test methods in VUE 3 Composition API (using vitest (preferably) or jest), knowing that methods are not accesible/exportable directly from the <script setup lang="ts"></script>
block?
Here is the HelloWorld.vue
:
<script setup lang="ts">
defineProps<{
msg: string
}>()
const sum = (a: number, b: number) => {
return a + b;
}
</script>
<template>
<div class="greetings">
<h1 class="green">{{ msg }}</h1>
<h3>
You’ve successfully created a project with
<a href="https://vitejs.dev/" target="_blank" rel="noopener">Vite</a> +
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>. What's next?
</h3>
</div>
</template>
Here is the HelloWorld.spec.ts
test file
import { describe, it, expect } from "vitest";
import { mount } from "@vue/test-utils";
import HelloWorld from "../HelloWorld.vue";
describe("HelloWorld", () => {
it("renders properly", () => {
const wrapper = mount(HelloWorld, { props: { msg: "Hello Vitest" } });
expect(wrapper.text()).toContain("Hello Vitest");
});
});
that tests component integration, passing and using props.
But how to test the sum
method?
I've tried:
test('adds 1 + 2 to equal 3', () => {
const wrapper = mount(HelloWorld, { props: { msg: "Hello Vitest" } });
// Access the sum function from the component instance
const instance = wrapper.vm;
const result = instance.sum(1, 2);
expect(result).toBe(3);
})
I've also tried const result = wrapper.vm.$options.setup().sum(1, 2);
Also these kind of approaches:
const vm = mount(HelloWorld, { props: { msg: "Hello Vitest" } }).vm;
const sumFunction = vm.$options.setup().sum;
...
etc.
I've made it work :)
IMPORTANT is that this solution does NOT require any change in the component that is being tested - no need for "exposing" methods
Here is HelloWorld.spec.ts
:
import { describe, it, test, expect } from "vitest";
import { mount } from "@vue/test-utils";
import HelloWorld from "../HelloWorld.vue";
describe("HelloWorld", () => {
const wrapper = mount(HelloWorld, { props: { msg: "Hello Vitest" } });
it("renders properly", () => {
expect(wrapper.text()).toContain("Hello Vitest");
});
test('sum: adds 1 + 2 to equal 3', () => {
// Access the Component method from the Composition API
const instance = wrapper.vm;
// The sum function is available within the instance
const sumFunction = (instance as any).sum;
const result = sumFunction(1, 2);
expect(result).toBe(3);
});
test('mul: muls 2 * 3 to equal 6', () => {
// Access the Component method from the Composition API
const instance = wrapper.vm;
// The mul function is available within the instance
const mulFunction = (instance as any).mul;
const result = mulFunction(2, 3);
expect(result).toBe(6);
})
});
and here is the component HelloWorld.vue
that is tested:
<script setup lang="ts">
defineProps<{
msg: string
}>()
const sum = (a: number, b: number) => {
return a + b;
}
const mul = (a: number, b: number) => {
return a * b;
}
</script>
<template>
<div class="greetings">
<h1 class="green">{{ msg }}</h1>
<h3>
You’ve successfully created a project with
<a href="https://vitejs.dev/" target="_blank" rel="noopener">Vite</a> +
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>. What's next?
</h3>
</div>
</template>
<style scoped>
h1 {
font-weight: 500;
font-size: 2.6rem;
position: relative;
top: -10px;
}
h3 {
font-size: 1.2rem;
}
.greetings h1,
.greetings h3 {
text-align: center;
}
@media (min-width: 1024px) {
.greetings h1,
.greetings h3 {
text-align: left;
}
}
</style>