DEV Community

peerhenry
peerhenry

Posted on

Integration testing Vue3 with Pinia

I was working on a Vue 3 project where I had to write an integration test with vue test utils for a component that relied on Pinia, while at the same time needing to set some initial state on a Pinia store before mounting that component. This caused a problem:

One solution would be to refactor the code so that the component would not rely on a store when mounting. But that would be too much work and would also be too restrictive for our codebase. We should not have to restrict access to stores in component lifecycle hooks!

So in this post another solution is presented, which is rendering the component inside of a wrapper component specifically designed for integration tests:

IntegrationTestWrapper.vue

<script setup lang="ts">
import { useMyStore } from '@/stores/myStore'

// === setup any Pinia stores here ===
useMyStore().$patch({ foo: 'bar' })
// ======

defineProps(['component'])
</script>

<template lang="pug">
component(:is="component")
</template>
Enter fullscreen mode Exit fullscreen mode

Then inside of our test we can write:

MyComponent.spec.ts

import { flushPromises, mount } from '@vue/test-utils'
import { createPinia } from 'pinia'

describe('MyComponent', () => {
  it('should mount', async () => {
    const wrapper = await mount(IntegrationTestWrapper, {
      props: {
        component: 'MyComponent', 
        anotherProp: 'abc', // will be passed to child component
      },
      global: {
        plugins: [createPinia()], // initializes Pinia
        stubs: { MyComponent }
      },
    })
    await flushPromises() // make sure all promises in lifecycle hooks are resolved
    expect(wrapper.exists()).toBe(true)
    // further assertions
  })
})
Enter fullscreen mode Exit fullscreen mode

Now everything is done in the correct order:

  • Pinia is created and initialized before IntegrationTestWrapper is mounted.
  • IntegrationTestWrapper initializes store state before mounting MyComponent.
  • Props given to IntegrationTestWrapper will be passed down to MyComponent.

Notes:

  • IntegrationTestWrapper is not rendering a slot because that would not receive the props.
  • You cannot just call createPinia() before configuring store state, you will receive an error that Pinia is not initialized with app.use(). It is only initialized in tests through mounting options.

Discussion (0)