Writing Pinia Tests with $subscribe
in Vue 3
This tutorial will guide you through writing tests for Pinia stores in Vue 3, especially when dealing with stores that use the $subscribe
method to react to state changes. We'll use a simplified example of a useUserStore
that depends on another store (useSettingsStore
) and updates its state when the settings store changes.
Table of Contents
- Introduction
- Setting Up the Environment
- Understanding the Stores
-
Writing the Test
- Mocking External Dependencies
- Creating a Test Component
- Setting Up the Test
- Testing the
$subscribe
Behaviour - Handling Errors
- Conclusion
Introduction
Pinia is the official state management library for Vue 3. It provides a simple and flexible API for managing global state in your application. When writing tests for Pinia stores, especially those that use $subscribe
to react to state changes, you need to mock dependencies and simulate state changes to ensure your store behaves as expected.
In this tutorial, we'll write tests for a useUserStore
that subscribes to changes in another store (useSettingsStore
). We'll use vitest
and @vue/test-utils
for testing.
Setting Up the Environment
Before writing tests, ensure you have the following dependencies installed:
npm install vitest @vue/test-utils @pinia/testing
-
vitest
: A fast and modern test runner for Vue 3. -
@vue/test-utils
: The official testing library for Vue components. -
@pinia/testing
: Provides utilities for testing Pinia stores.
Understanding the Stores
useUserStore
This store depends on another store:
-
useSettingsStore
: Manages user settings.
The useUserStore
subscribes to changes in the useSettingsStore
using $subscribe
and updates its userPreferences
state whenever the settings store changes.
export const useUserStore = defineStore('user-store', () => {
const userPreferences = ref<UserPreferences>({ theme: 'light', notifications: true })
const settingsStore = useSettingsStore()
const updatePreferences = () => {
userPreferences.value = {
theme: settingsStore.theme,
notifications: settingsStore.notificationsEnabled,
}
}
// Subscribe to changes in the settings store
settingsStore.$subscribe((_mutation, _state) => {
updatePreferences()
})
return {
userPreferences,
}
})
useSettingsStore
This store manages user settings like theme and notifications.
export const useSettingsStore = defineStore('settings-store', () => {
const theme = ref<'light' | 'dark'>('light')
const notificationsEnabled = ref<boolean>(true)
const toggleTheme = () => {
theme.value = theme.value === 'light' ? 'dark' : 'light'
}
const toggleNotifications = () => {
notificationsEnabled.value = !notificationsEnabled.value
}
return {
theme,
notificationsEnabled,
toggleTheme,
toggleNotifications,
}
})
Writing the Test
Mocking External Dependencies
To test useUserStore
, we need to mock external dependencies.
vi.mock('@cct/notifications', () => ({
useNotifications: () => ({
error: vi.fn(),
}),
}))
Creating a Test Component
We'll create a test component that uses the stores to simulate real-world usage.
const TestComponent = defineComponent({
setup() {
const userStore = useUserStore()
const settingsStore = useSettingsStore()
return {
userStore,
settingsStore,
}
},
template: '<div></div>',
})
Setting Up the Test
We'll use createTestingPinia
to set up the test environment.
ref
please note I did not use
setActivePinia(createPinia())
The simple reason is flexibility of creating multiple initial state.
describe('useUserStore', () => {
let wrapper: any
let userStore: any
let settingsStore: any
beforeEach(() => {
wrapper = mount(TestComponent, {
global: {
plugins: [
createTestingPinia({
initialState: {
'user-store': {
userPreferences: { theme: 'light', notifications: true },
},
'settings-store': {
theme: 'light',
notificationsEnabled: true,
},
},
createSpy: vi.fn,
}),
],
},
})
// Get store instances
userStore = useUserStore()
settingsStore = useSettingsStore()
})
})
Testing the $subscribe
Behavior
We'll test that the userPreferences
state in useUserStore
updates correctly when the settingsStore
changes.
it('should update userPreferences when settingsStore changes', async () => {
// Change the theme in the settings store
settingsStore.toggleTheme()
// Verify the userPreferences state
expect(userStore.userPreferences).toEqual({
theme: 'dark',
notifications: true,
})
// Toggle notifications
settingsStore.toggleNotifications()
// Verify the userPreferences state
expect(userStore.userPreferences).toEqual({
theme: 'dark',
notifications: false,
})
})
Handling Errors
We'll also test that the store handles errors gracefully.
it('should handle errors gracefully', async () => {
// Simulate an error in the settings store
settingsStore.theme = 'invalid-theme' as any
// Verify the userPreferences state remains unchanged
expect(userStore.userPreferences).toEqual({
theme: 'light',
notifications: true,
})
})
Conclusion
Testing Pinia stores, especially those with $subscribe
, requires careful mocking of dependencies and simulating state changes. By following this tutorial, you should be able to write robust tests for your Pinia stores in Vue 3.
For more advanced testing scenarios, refer to the official documentation for Pinia and Vitest.
Top comments (0)