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:
- The component could not mount before a Pinia store was configured,
- The Pinia store could not be configured before the component would be mounted. Read why in this paragraph of the official docs of vue test utils.
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>
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
})
})
Now everything is done in the correct order:
- Pinia is created and initialized before
IntegrationTestWrapper
is mounted. -
IntegrationTestWrapper
initializes store state before mountingMyComponent
. - Props given to
IntegrationTestWrapper
will be passed down toMyComponent
.
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 withapp.use()
. It is only initialized in tests through mounting options.
Top comments (0)