Skip to content

Vue Component Testing with Cypress

This lesson covers Cypress component testing specifically for Vue: setup, cy.mount(), testing props and emitted events, and working with Pinia or Vuex state.

Mounting a Vue Component

The @cypress/vue mount function renders a Vue component using your project's real Vue setup. Props are passed as a mount option, and emitted events can be asserted on directly since Cypress observes the actual DOM and component instance.

import Counter from './Counter.vue';

it('increments the count when clicked', () => {
  cy.mount(Counter, { props: { initialCount: 0 } });
  cy.get('[data-cy="count"]').should('have.text', '0');
  cy.get('[data-cy="increment"]').click();
  cy.get('[data-cy="count"]').should('have.text', '1');
});

Props are passed via a mount options object, rather than as JSX attributes like in React.

Vue Component Testing Syntax

cy.mount(MyComponent, { props: { ... } });
cy.mount(MyComponent, {
  global: { plugins: [pinia] },
});
  • The props mount option passes props exactly like using the component in a template.
  • The global mount option registers plugins, directives, or global components needed by the component.
  • Component spec files typically use a .cy.js/.cy.ts extension, colocated with the .vue file.
  • Emitted custom events can be asserted using a stub passed via props.onEventName.

Vue Component Testing Cheat Sheet

Common patterns for testing Vue components with Cypress.

Goal Approach
Test a component with props cy.mount(Component, { props: { ... } })
Test an emitted event Pass onEventName: cy.stub().as('name') as a prop
Test a component using Pinia global: { plugins: [pinia] }
Test a component using Vue Router global: { plugins: [router] }
Test slots cy.mount(Component, { slots: { default: 'Content' } })

Testing Emitted Events

In Vue's Options and Composition APIs, components communicate upward by emitting custom events. Cypress component testing lets you pass a stub as an event handler prop and assert it was called correctly, mirroring how a parent component would actually consume the event.

it('emits a save event with the form data', () => {
  const onSave = cy.stub().as('onSave');
  cy.mount(ProfileForm, { props: { onSave } });
  cy.get('[data-cy="name"]').type('Jane');
  cy.get('[data-cy="submit"]').click();
  cy.get('@onSave').should('have.been.calledWith', { name: 'Jane' });
});

Testing Components That Use Pinia

Components reading from or writing to a Pinia store need an active Pinia instance during the mount, registered via the global.plugins mount option, optionally pre-populated with specific state for the scenario under test.

import { createTestingPinia } from '@pinia/testing';

cy.mount(CartSummary, {
  global: {
    plugins: [createTestingPinia({
      initialState: { cart: { items: [{ id: 1, price: 25 }] } },
    })],
  },
});

Common Mistakes

  • Passing props as component attributes directly instead of inside the props mount option.
  • Forgetting to register required plugins (Pinia, Vue Router) via global.plugins, causing runtime errors.
  • Testing a component's internal Pinia/Vuex mutations directly instead of asserting on the resulting rendered output.
  • Not using createTestingPinia (or equivalent) for isolated, predictable store state per test.

Key Takeaways

  • cy.mount() for Vue accepts props, slots, and global plugins via a mount options object.
  • Emitted events can be tested by passing a stub as the corresponding onEventName prop.
  • Components depending on Pinia or Vue Router need those registered via global.plugins.
  • createTestingPinia provides isolated, controllable store state for component tests.

Pro Tip

When testing components that consume a shared store, use createTestingPinia (or a stubbed Vuex store) with an explicit initialState per test rather than a shared, mutable store instance, this keeps every component test fully isolated and prevents state leaking between tests.