The Vue Pinia function becomes undefined during the execution of unit tests in the onMounted hook

My component and Pinia store are working flawlessly in the browser and E2E tests using Cypress, but they fail when it comes to running unit tests. I am utilizing vue-testing-utils and vitest for testing.

In the unit test, calling a function from the store works fine when triggered by a button click. However, if the function is called within the mounted or main script, the test fails.

src/components/UsersComponent.vue

<script setup>
import { onMounted } from 'vue'
import { useUsersStore } from '@/stores/users.store'

const usersStore = useUsersStore()

onMounted(() => {
  usersStore.resetStatus()
})

function changeStatus() {
  usersStore.changeStatus()
}
</script>

<template>
  <div>
    <p>Status: {{ usersStore.status }}</p>
    <button @click="changeStatus()">Change Status</button>
  </div>
</template>

src/stores/users.store.js

import { defineStore } from 'pinia'
import { usersAPI } from '@/gateways'

export const useUsersStore  = defineStore({
  id: 'users',
  persist: true,

  state: () => ({
    status: 'ready',
  }),

  actions: {
    resetStatus() {
      this.status = 'ready'
    },
    changeStatus() {
      this.status = 'loading'
    },
  },
})

src/components/tests/UsersComponent.spec.js

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { mount } from '@vue/test-utils'
import { createTestingPinia } from '@pinia/testing'

import UsersComponent from '@/components/UsersComponent.vue'
import { useUsersStore } from '@/stores/users.store'

const wrapper = mount(UsersComponent, {
  global: {
    plugins: [createTestingPinia({ createSpy: vi.fn() })],
  },
})
const usersStore = useUsersStore()

describe('UsersComponent', () => {
  it('store function is called', async () => {
    // arrange
    const spy = vi.spyOn(usersStore, 'resetStatus')
    const button = wrapper.find('button')

    // act
    await button.trigger('click')

    // assert
    expect(spy).toHaveBeenCalled()
  })
})

The unit tests throw two different errors. The first error occurs as a console log when trying to run the function inside onMounted(), while the second error is what vitest returns.

stderr | unknown test
[Vue warn]: Unhandled error during execution of mounted hook 
  at <UsersComponent ref="VTU_COMPONENT" >
  at <VTUROOT>
 FAIL  src/components/__tests__/UsersComponent.spec.js [ src/components/__tests__/UsersComponent.spec.js ]
TypeError: usersStore.resetStatus is not a function
 ❯ src/components/UsersComponent.vue:16:14
     16|
     17| <template>
     18|   <div>
       |  ^
     19|     <p>Status: {{ usersStore.status }}</p>
     20|     <button @click="changeStatus()">Change Status</button>

Although this example may seem basic, I'm curious about how to integrate store functions inside onMounted() or similar places without causing issues in all unit tests.

Answer №1

Here's a handy code snippet for you:

describe('UsersComponent',  () => {
  it('check if changeStatus function is triggered', async () => {
    const wrapper = mount(UsersComponent, {
      mounted: vi.fn(), // Mock the onMounted method
      global: {
        plugins: [createTestingPinia({
          initialState: { // Set initial state
            users: { status: 'ready' }, 
          }
        })]
      }
    })  
    // Spy on the method being called...
    const spy = vi.spyOn(wrapper.vm, 'changeStatus');
    
    wrapper.vm.changeStatus();
    
    expect(spy).toHaveBeenCalled();
    expect(spy).toHaveBeenCalledTimes(1);
  })
})

Answer №2

I prefer using vitest, pinia, and testing-library (which is based on vue-test-utils). In my opinion, there is no necessity for:

  • mocking onMounted: the goal is not to verify that onMounted is called => as this is an internal Vue test

  • using vi.spyOn; by default, pinia mocks all methods. You can simply do this in your test:

    const usersStore = useUsersStore();

    expect(usersStore.resetStatus).toHaveBeenCalledTimes(1);

I follow the same approach for all onBeforeMount and onMounted functions that make HTTP calls from the store

onMounted(async() => {
  await Promise.all([
    valuesStore.getAllValues(),
    serviceRequestStore.getServiceRequest(),
  ]);
});

In the test:

it('fetches the service request from the serviceRequest store', () => {
  renderTheComponent();

  const serviceRequestStore = useServiceRequestStore();
  expect(serviceRequestStore.getServiceRequest).toHaveBeenCalledTimes(1);
});

I believe this method should also be applicable with vue-test-utils. All hooks in my components are asynchronous and return promises.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

React does not auto-populate form data

I am working on a simple project using Next.js with a basic form. However, when I try to submit the form and log the form data in the console, it shows up as an empty object. I'm puzzled by this issue. Here is my code: import styles from &apos ...

What is the reason behind allowing JavaScript to perform mathematical operations with a string type number?

let firstNum = 10; let secondNum = "10"; console.log(firstNum * secondNum); // Result: 100 console.log(secondNum * secondNum); // Result: 100 ...

Step-by-step guide on adding a gallery image in Node.js

I need help with posting my gallery image to a nodeJs server. Here is the code I am currently using: vm.getImageSaveContactInst = function() { var options = { maximumImagesCount: 1, // Only selecting one image for this example width ...

Retrieving data in Next.js

Exploring various techniques to retrieve information from APIs in next.js. Options include getServerSideProps, getStaticPaths, getStaticProps, Incremental Static Regeneration, and client-side rendering. If I need to send requests to the backend when a s ...

Is there a way to direct the particles towards the camera while rotating, and trigger an alert message when they intersect in Three.js?

I'm working on implementing an event handler for particles that triggers an alert message on a sphere, always facing the camera. I am looking to achieve something similar to this demo (and making sure it works on IE 9+). Here is the code snippet I ha ...

JavaScript: automatically copy text content to clipboard and include the original source link

My expertise lies in simple website development using HTML, CSS, and WordPress. While I can work with JavaScript to some extent, I admit that I'm not proficient in it. Recently, a client approached me with a specific request. They want the functional ...

Issue with Sending Form Data on Safari/Firefox iOS

While this is effective on most platforms, it seems to encounter compatibility issues with Safari and Firefox on iOS: jQuery("#gform_1").one('submit', function(event) { const form = document.getElementById("gform_1"); const firstName = d ...

When a javascript web-service client attempts to send a CORS POST request, it results in receiving an HTTP

The other day, I successfully set up a web-service (both client & server). The client was able to send requests using XMLHttpRequest() without any issues. However, today I am attempting to switch the method of sending requests from that to jQuery's $. ...

Validate the Start and Finish Date in a DatePicker without the need for a plugin

Utilizing Asp.Net MVC 4 in combination with JqueryUI. I have two textboxes that are equipped with datepickers. I am in need of validation where the start date can be equal to the finish date, but the finish date cannot be before the start date. Here is t ...

Prevent the execution of Javascript in textarea fields

In my web application, I have a textarea where users can input any text that is saved as a string in the database. The issue arises when users enter Javascript code into the textarea, as it will execute when viewed on the saved data page. Is there a univ ...

Enhance your browsing experience with a JavaScript bookmarklet that allows you to easily search through

Struggling to develop a JS Bookmarklet that scans the source code of the current page for a specific code like "G1_Value_client." If found, I need it to trigger alert A; if not found, trigger alert B. Seeking assistance as I am facing some challenges with ...

Displaying Grunt Command-Line Output in Browser

Is there a straightforward method to display the command-line output of a grunt task in a browser? Essentially, I want to showcase the input and/or output of a command line process within a browser window. While I am aware that I could develop a custom app ...

Using localStorage.getItem() to select SVG <g> elements

On my website, I have incorporated two <iframe> elements that link to separate HTML files containing SVG images. When a user clicks on an svg element in file1.html, an ID number is generated and stored using the code: localStorage.setItem("element1i ...

Is it possible to implement specific javascript code exclusively on mobile devices?

Is there a way to restrict the execution of specific javascript code to only screen sizes below 900 pixels? My goal is to implement left and right navigation buttons for mobile devices. However, I encountered issues when trying to use matchMedia as it di ...

Difficulty in loading the uploaded API data into the template

I am facing an issue with a service that retrieves user data based on the Token stored in localStorage. The data is returned correctly until it reaches my component. The problem lies in the code snippet present in my component.ts file: https://i.sstatic.n ...

What is the process for converting this Greasemonkey code to JavaScript specifically for Android devices?

Having trouble loading a page and running a JavaScript code on it? Don't worry, you're not alone. I came across a Greasemonkey script that does the trick, but now I'm struggling to make it work on Android. It's probably because of my la ...

Tips for Embedding External JavaScript and URLs in Joomla Protostar Template

I'm interested in incorporating a hamburger menu into my Joomla protostar template, similar to the one featured on this URL: ['https://codepen.io/PaulVanO/pen/GgGeyE'] This will provide me with a fullscreen hamburger menu for both desktop ...

Vuex - Avoid modifying the vuex store state directly, unless done within mutation handlers. Make sure any state changes are made

When I call a store action from the main layout (default.vue in Nuxt.js), and then call a state mutation within the mutations export, I encounter an error. Can anyone help me figure out why? The error message in console: http://prntscr.com/rwvfjf Here is ...

Is there a way to connect a controller to rootDocument without using a directive?

I'm currently developing a custom plugin for bootstrapping my Angular application manually, using the document as the root. I want to attach a controller to my root without utilizing the ng-controller directive in order to create a global controller e ...

What is the best way to transmit multiple data in JSON format with Ajax and retrieve it in PHP?

When working with .js, I encountered an issue while trying to send multiple data in json format using ajax and receiving them in my php file. In the javascript part: $("#mybtn").on('click', function(){ $.ajax({ type:'POST' ...