Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit test VUE 3 Composition API methods?

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.

like image 331
Sinisa Rudan Avatar asked Sep 01 '26 17:09

Sinisa Rudan


1 Answers

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>
like image 109
Sinisa Rudan Avatar answered Sep 05 '26 05:09

Sinisa Rudan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!