@sourceallies/vue-testing-library-stubs
v1.0.1
Published
Contains stubbing functions for Vue components to unit test components without requiring the full component to be loaded.
Downloads
1,357
Readme
Vue Testing Library Stubs
Contains stubbing functions for Vue components to unit test components without requiring the full component to be loaded.
Why Use This Library?
When testing Vue components, you often need to test a parent component's behavior without rendering all of its child components. This library provides pre-built stub components that:
- Simplify Component Isolation: Test parent components without worrying about child component complexity or dependencies
- Verify Props Passing: Automatically display props in a testable format (e.g.,
propName-value) - Test Event Handling: Create stubs that emit events with specific payloads, allowing you to verify parent components handle events correctly
- Reduce Test Complexity: Avoid importing and configuring numerous child components in your tests
- Support Multiple Testing Libraries: Works seamlessly with both
@vue/test-utilsand@testing-library/vue
Common Use Cases
- Testing that a parent component passes the correct props to child components
- Verifying that a parent component correctly handles events emitted by children
- Testing form validation flows where child components expose validation methods
- Isolating unit tests from complex or slow-to-render child components
Installation
npm install --save-dev @sourceallies/vue-testing-library-stubsDevelopment
This project is written in TypeScript.
Prerequisites
- Node.js >= 18.0.0
- npm
Setup
npm installBuilding
Build the TypeScript source to JavaScript:
npm run buildLocal testing of stub functions using another project
Ensure package is not already installed in the project testing the changes.
npm uninstall @sourceallies/vue-testing-library-stubsEnsure Vue Testing Library Stubs project is using the same node version as the project testing the changes.
node -vCreate a symlink for the project testing the changes to connect to.
npm linkIn project testing the changes link the package
npm link @sourceallies/vue-testing-library-stubsThe compiled output will be in the dist/ directory.
Project Structure
src/
├── index.ts # Main entry point with stub functions
└── types.ts # TypeScript type definitionsUsage
This library provides various stub components for testing Vue applications with @vue/test-utils or @testing-library/vue.
Basic Stub
Create a simple stub component for testing:
Using @vue/test-utils:
import { mount } from '@vue/test-utils';
import { getStub } from '@sourceallies/vue-testing-library-stubs';
const wrapper = mount(MyParentComponent, {
global: {
stubs: {
...getStub('ChildComponent')
}
}
});
// Verify the stub was rendered
expect(wrapper.text()).toContain('ChildComponent-stub');Using @testing-library/vue:
import { render, screen } from '@testing-library/vue';
import { getStub } from '@sourceallies/vue-testing-library-stubs';
render(MyParentComponent, {
global: {
stubs: {
...getStub('ChildComponent')
}
}
});
// Verify the stub was rendered
expect(screen.getByText('ChildComponent-stub')).toBeVisible();Stub With Props
Test components that pass props to child components:
Using @vue/test-utils:
import { mount } from '@vue/test-utils';
import { getStubWithProps } from '@sourceallies/vue-testing-library-stubs';
const wrapper = mount(MyParentComponent, {
global: {
stubs: {
...getStubWithProps('ChildComponent', 'title', 'count', 'config')
}
}
});
// Verify props are displayed (automatically converts to {propName}-{value})
expect(wrapper.text()).toContain('title-Hello');
expect(wrapper.text()).toContain('count-42');Using @testing-library/vue:
import { render, screen } from '@testing-library/vue';
import { getStubWithProps } from '@sourceallies/vue-testing-library-stubs';
render(MyParentComponent, {
global: {
stubs: {
...getStubWithProps('ChildComponent', 'title', 'count', 'config')
}
}
});
// Verify props are displayed (automatically converts to {propName}-{value})
expect(screen.getByText(/title-Hello/)).toBeVisible();
expect(screen.getByText(/count-42/)).toBeVisible();Stub With Event Emitters
Test components that listen to child component events:
Using @vue/test-utils:
import { mount } from '@vue/test-utils';
import { getEmittingStub } from '@sourceallies/vue-testing-library-stubs';
const wrapper = mount(MyParentComponent, {
global: {
stubs: {
...getEmittingStub('ChildComponent', 'save', { id: 123 })
}
}
});
// Trigger the event by finding the button with text 'save'
const button = wrapper.find('button'); // Button text is 'save'
await button.trigger('click');
// Verify parent component handled the event
expect(wrapper.emitted('save')).toBeTruthy();
expect(wrapper.emitted('save')?.[0]).toEqual([{ id: 123 }]);Using @testing-library/vue:
import { render, screen } from '@testing-library/vue';
import userEvent from '@testing-library/user-event';
import { getEmittingStub } from '@sourceallies/vue-testing-library-stubs';
const { emitted } = render(MyParentComponent, {
global: {
stubs: {
...getEmittingStub('ChildComponent', 'save', { id: 123 })
}
}
});
// Trigger the event
const button = screen.getByRole('button', { name: 'save' });
await userEvent.click(button);
// Verify parent component handled the event with the correct value
expect(emitted()).toHaveProperty('save');
expect(emitted().save[0]).toEqual([{ id: 123 }]);Stub With Props and Events
Combine props and event emission:
Using @vue/test-utils:
import { mount } from '@vue/test-utils';
import { getEmittingStubWithProps } from '@sourceallies/vue-testing-library-stubs';
const wrapper = mount(MyParentComponent, {
global: {
stubs: {
...getEmittingStubWithProps(
'ChildComponent',
'update',
[{ newValue: 'test' }],
'title',
'isActive'
)
}
}
});
// Trigger the event
const button = wrapper.find('button'); // Button text is 'update'
await button.trigger('click');
// Verify the event was emitted with the correct value
expect(wrapper.emitted('update')).toBeTruthy();
expect(wrapper.emitted('update')?.[0]).toEqual([{ newValue: 'test' }]);Using @testing-library/vue:
import { render, screen } from '@testing-library/vue';
import userEvent from '@testing-library/user-event';
import { getEmittingStubWithProps } from '@sourceallies/vue-testing-library-stubs';
const { emitted } = render(MyParentComponent, {
global: {
stubs: {
...getEmittingStubWithProps(
'ChildComponent',
'update',
[{ newValue: 'test' }],
'title',
'isActive'
)
}
}
});
// Verify both stub and props are rendered
expect(screen.getByText('ChildComponent-stub')).toBeVisible();
// Trigger the event
const button = screen.getByRole('button', { name: 'update' });
await userEvent.click(button);
// Verify the event was emitted with the correct value
expect(emitted()).toHaveProperty('update');
expect(emitted().update[0]).toEqual([{ newValue: 'test' }]);Multiple Events Stub
Test components with multiple event handlers:
Using @vue/test-utils:
import { mount } from '@vue/test-utils';
import { getMultiEmittingStubWithProps, type EmittedEvent } from '@sourceallies/vue-testing-library-stubs';
const events: EmittedEvent[] = [
{ name: 'save', value: { id: 1 } },
{ name: 'cancel' },
{ name: 'delete', value: { id: 1 } }
];
const wrapper = mount(MyParentComponent, {
global: {
stubs: {
...getMultiEmittingStubWithProps('ChildComponent', events, 'title', 'status')
}
}
});
// Each event gets its own button
const buttons = wrapper.findAll('button');
await buttons[0].trigger('click'); // Triggers 'save'
await buttons[1].trigger('click'); // Triggers 'cancel'
// Verify events were emitted with correct values
expect(wrapper.emitted('save')).toBeTruthy();
expect(wrapper.emitted('save')?.[0]).toEqual([{ id: 1 }]);
expect(wrapper.emitted('cancel')).toBeTruthy();
expect(wrapper.emitted('cancel')?.[0]).toEqual([undefined]);Using @testing-library/vue:
import { render, screen } from '@testing-library/vue';
import userEvent from '@testing-library/user-event';
import { getMultiEmittingStubWithProps, type EmittedEvent } from '@sourceallies/vue-testing-library-stubs';
const events: EmittedEvent[] = [
{ name: 'save', value: { id: 1 } },
{ name: 'cancel' },
{ name: 'delete', value: { id: 1 } }
];
const { emitted } = render(MyParentComponent, {
global: {
stubs: {
...getMultiEmittingStubWithProps('ChildComponent', events, 'title', 'status')
}
}
});
// Each event gets its own button
const saveButton = screen.getByRole('button', { name: 'save' });
const cancelButton = screen.getByRole('button', { name: 'cancel' });
await userEvent.click(saveButton);
await userEvent.click(cancelButton);
// Verify events were emitted with correct values
expect(emitted()).toHaveProperty('save');
expect(emitted().save[0]).toEqual([{ id: 1 }]);
expect(emitted()).toHaveProperty('cancel');
expect(emitted().cancel[0]).toEqual([undefined]);Validation Stubs
Test parent components that call child validation methods:
Using @vue/test-utils:
import { mount } from '@vue/test-utils';
import { getExposedValidateStub } from '@sourceallies/vue-testing-library-stubs';
const wrapper = mount(MyFormComponent, {
global: {
stubs: {
...getExposedValidateStub('InputComponent', true) // true = validation passes
}
}
});
// After calling validate on the child component
expect(wrapper.text()).toContain('InputComponent-stub-validated-true');Using @testing-library/vue:
import { render, screen } from '@testing-library/vue';
import { getExposedValidateStub } from '@sourceallies/vue-testing-library-stubs';
render(MyFormComponent, {
global: {
stubs: {
...getExposedValidateStub('InputComponent', true) // true = validation passes
}
}
});
// After calling validate on the child component
expect(screen.getByText(/InputComponent-stub-validated-true/)).toBeVisible();Template for Exposed Functions
Test components that use child component's exposed functions:
Using @vue/test-utils:
import { mount } from '@vue/test-utils';
import { getTemplateComponentForExposedFunction } from '@sourceallies/vue-testing-library-stubs';
const props = { value: 'test', enabled: true };
const componentWithExposed = getTemplateComponentForExposedFunction(
ChildComponent,
'reset',
props
);
const wrapper = mount(componentWithExposed, {
props
});
// Click button to trigger the exposed function
await wrapper.find('button').trigger('click');Using @testing-library/vue:
import { render, screen } from '@testing-library/vue';
import userEvent from '@testing-library/user-event';
import { getTemplateComponentForExposedFunction } from '@sourceallies/vue-testing-library-stubs';
const props = { value: 'test', enabled: true };
const componentWithExposed = getTemplateComponentForExposedFunction(
ChildComponent,
'reset',
props
);
render(componentWithExposed, {
props
});
// Click button to trigger the exposed function
const button = screen.getByRole('button', { name: 'reset' });
await userEvent.click(button);API Reference
getStub(componentName: string)
Creates a basic stub component with no props or events.
Parameters:
componentName(string): The name of the component to stub
Returns: An object with a single key (the component name) containing the stub configuration
Example:
const stub = getStub('MyComponent');
// Renders: <div>MyComponent-stub</div>getStubWithProps(componentName: string, ...props: (string | object)[])
Creates a stub component that displays its props in a testable format.
Parameters:
componentName(string): The name of the component to stub...props(string | object): The prop names to accept and display
Returns: An object with a single key (the component name) containing the stub configuration
Features:
- Automatically converts hyphenated prop names to camelCase
- Displays props as
propName-valuein the rendered output - Handles object props by JSON stringifying them
- Shows function names for array of functions (useful for validation rules)
Example:
const stub = getStubWithProps('MyComponent', 'title', 'count', 'is-active');
// Renders props as: title-Hello, count-42, isActive-truegetEmittingStub(componentName: string, emittedEvent: string, ...emittedValues: any[])
Creates a stub component that emits a single event when a button is clicked.
Parameters:
componentName(string): The name of the component to stubemittedEvent(string): The name of the event to emit...emittedValues(any[]): The values to emit with the event
Returns: An object with a single key (the component name) containing the stub configuration
Features:
- Creates a button with the event name as its label
- Emits the specified event with the provided values when clicked
- Includes
validate(),reset(), andresetValidation()methods that return valid results
Example:
const stub = getEmittingStub('MyComponent', 'save', { id: 123 });
// Button click emits: 'save' event with { id: 123 }getEmittingStubWithProps(componentName: string, emittedEvent: string, emittedValues: any[], ...props: (string | object)[])
Creates a stub component that combines props display and event emission.
Parameters:
componentName(string): The name of the component to stubemittedEvent(string): The name of the event to emitemittedValues(any[]): Array of values to emit with the event...props(string | object): The prop names to accept and display
Returns: An object with a single key (the component name) containing the stub configuration
Features:
- Combines functionality of
getStubWithPropsandgetEmittingStub - Displays props in testable format
- Emits events with specified values
- Includes validation methods
Example:
const stub = getEmittingStubWithProps('MyComponent', 'update', [{ newValue: 'test' }], 'title', 'isActive');getMultiEmittingStubWithProps(componentName: string, events: EmittedEvent[], ...props: (string | object)[])
Creates a stub component that can emit multiple different events.
Parameters:
componentName(string): The name of the component to stubevents(EmittedEvent[]): Array of event configurations withnameand optionalvalue...props(string | object): The prop names to accept and display
Type Definitions:
type EmittedEvent = {
name: string;
value?: any;
};Returns: An object with a single key (the component name) containing the stub configuration
Features:
- Creates a separate button for each event
- Each button is labeled with its event name
- Supports events with or without values
Example:
const events: EmittedEvent[] = [
{ name: 'save', value: { id: 1 } },
{ name: 'cancel' },
{ name: 'delete', value: { id: 1 } }
];
const stub = getMultiEmittingStubWithProps('MyComponent', events, 'title');getExposedValidateStub(componentName: string, isValid: boolean = true)
Creates a stub component with an exposed validate() method for testing form validation.
Parameters:
componentName(string): The name of the component to stubisValid(boolean, optional): Whether validation should pass (default:true)
Returns: An object with a single key (the component name) containing the stub configuration
Features:
- Exposes a
validate()method that returns{ valid: boolean, errors: [] } - Updates the rendered output to show
{componentName}-stub-validated-trueorfalse - Includes
resetValidation()method
Example:
const stub = getExposedValidateStub('InputComponent', true);
// After calling validate(): renders "InputComponent-stub-validated-true"getExposedValidateStubWithProps(componentName: string, isValid: boolean = true, ...props: (string | object)[])
Creates a stub component that combines validation testing with props display.
Parameters:
componentName(string): The name of the component to stubisValid(boolean, optional): Whether validation should pass (default:true)...props(string | object): The prop names to accept and display
Returns: An object with a single key (the component name) containing the stub configuration
Features:
- Combines functionality of
getExposedValidateStubandgetStubWithProps - Displays props in testable format
- Exposes validation methods
Example:
const stub = getExposedValidateStubWithProps('InputComponent', true, 'value', 'label');getTemplateComponentForExposedFunction(componentAlias: Component, exposedFn: string, props?: object)
Creates a wrapper component that calls an exposed function on a child component.
Parameters:
componentAlias(Component): The actual component to wrapexposedFn(string): The name of the exposed function to callprops(object, optional): Props to pass to the child component
Returns: A Vue component configuration that wraps the provided component
Features:
- Creates a button that triggers the exposed function
- Button is labeled with the function name
- Passes props to the child component
- Useful for testing components that use
expose()ordefineExpose()
Example:
const wrapper = getTemplateComponentForExposedFunction(ChildComponent, 'reset', { value: 'test' });
// Renders ChildComponent with a button that calls reset() when clickedLicense
MIT
