DEV Community

Cover image for Writing Pinia Store Tests with `$subscribe` in Vue 3
Ajinkya Borade
Ajinkya Borade

Posted on • Edited on

Writing Pinia Store Tests with `$subscribe` in Vue 3

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

  1. Introduction
  2. Setting Up the Environment
  3. Understanding the Stores
  4. Writing the Test
    • Mocking External Dependencies
    • Creating a Test Component
    • Setting Up the Test
    • Testing the $subscribe Behaviour
    • Handling Errors
  5. 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
Enter fullscreen mode Exit fullscreen mode
  • 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,
  }
})
Enter fullscreen mode Exit fullscreen mode

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,
  }
})
Enter fullscreen mode Exit fullscreen mode

Writing the Test

Mocking External Dependencies

To test useUserStore, we need to mock external dependencies.

vi.mock('@cct/notifications', () => ({
  useNotifications: () => ({
    error: vi.fn(),
  }),
}))
Enter fullscreen mode Exit fullscreen mode

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>',
})
Enter fullscreen mode Exit fullscreen mode

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()
  })
})
Enter fullscreen mode Exit fullscreen mode

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,
  })
})
Enter fullscreen mode Exit fullscreen mode

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,
  })
})
Enter fullscreen mode Exit fullscreen mode

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)