With Vue Test Utils' mount(), how do you pass props, slots and attrs to a Vue component, and change its props later?
answer
- second argument to mount()
- three keys, three kinds of input
- slot value: string, h() vnode or component
- same key in props and attrs
- setProps lives on the root wrapper only
basics
~20 sPass an options object as mount()'s second argument: props for declared props, slots keyed by slot name (a string, an h() vnode or a component) and attrs for undeclared attributes. Change props later with await wrapper.setProps({...}) on the root wrapper.
solid answer
~50 s`mount(Component, options)` takes a mounting-options object. `props` supplies declared props. `slots` maps slot names to content — `default`, a named key such as `footer`, or a scoped slot — given as a string template, an `h()` vnode, a function or an imported component. `attrs` supplies attributes the component does not declare, which by default fall through to its root element. Vue Test Utils actually merges `attrs` and `props` into one vnode props object (props last), so Vue itself decides what is a prop, and a key present in both resolves to the `props` value. After mounting, `wrapper.setProps({ title: 'New' })` merges new values into the root's props and returns a promise you await before asserting. It works only on the wrapper `mount()` returned; calling it on a `findComponent()` child throws `You can only use setProps on your mounted component`.
code
ts · 23 linesimport { mount } from '@vue/test-utils'
import { h } from 'vue'
import InvoiceCard from './InvoiceCard.vue'
import StatusBadge from './StatusBadge.vue'
test('renders props, slots and fallthrough attrs', async () => {
const wrapper = mount(InvoiceCard, {
props: { title: 'Invoice #42', total: 120 },
slots: {
default: 'Line items go here',
footer: h('button', { type: 'button' }, 'Pay now'),
badge: StatusBadge
},
attrs: { id: 'invoice-42', 'data-test': 'card' }
})
expect(wrapper.get('h2').text()).toBe('Invoice #42')
expect(wrapper.attributes('id')).toBe('invoice-42')
await wrapper.setProps({ title: 'Invoice #43' })
expect(wrapper.get('h2').text()).toBe('Invoice #43')
expect(wrapper.props('total')).toBe(120)
})go deeper
Recall the three mounting options by name and what each feeds: props for declared inputs, slots by slot name, attrs for everything undeclared. Know that setProps is awaited and belongs to the root wrapper.
Explain that mount builds a wrapper parent which renders your component from one merged props object, so Vue decides the prop-or-attr split, and that setProps merges into that reactive object.
Use mounting options to express a realistic parent: real slot content, realistic attrs for accessibility hooks, and prop changes driven through setProps or interaction rather than by reaching into child instances.
Set suite conventions so mounting mirrors real usage: shared factory helpers that build props and slots, no direct instance mutation, and review rules that reject setProps workarounds on children.
## What mount() is and what its options are for **Vue Test Utils** (the `@vue/test-utils` package, version 2.x for Vue 3) renders a component into a detached DOM and hands back a **`VueWrapper`** — an object with methods such as `find()`, `text()`, `props()`, `emitted()` and `setProps()`. Its entry point is `mount(Component, options)`. The second argument is the **mounting options** object, and three of its keys describe what a parent would normally hand the component: | option | what it feeds | typical value | |---|---|---| | `props` | props the component declares with `defineProps` or `props:` | `{ title: 'Invoice #42', total: 120 }` | | `slots` | the component's `<slot>` outlets | `{ default: 'text', footer: h('button', 'Pay') }` | | `attrs` | anything the component does **not** declare | `{ id: 'card-1', 'data-test': 'card' }` | Other options exist (`global`, `shallow`, `attachTo`, `data`), but these three are how you set up the component's inputs. ## props: the declared inputs `props` is a plain object of prop values. Under the hood Vue Test Utils creates a small wrapper parent that renders your component with these values, so the component receives them exactly as it would from a real parent: types are validated, defaults apply to missing keys and `required` props warn when absent. - Pass real values, including objects and functions; they are not stringified. - The older Vue Test Utils 1 option name `propsData` is still accepted for backward compatibility, but `props` is the current name. ## slots: default, named and scoped The `slots` object is keyed by slot name. `default` fills the unnamed `<slot />`; any other key fills the matching `<slot name="...">`. Each value can take several forms: - **a string** — treated as a template, so `'<strong>Hi</strong>'` renders an element, not escaped text; - **an `h()` vnode** — `h('button', { type: 'button' }, 'Pay now')`; - **a component** — an imported SFC is rendered in the slot; - **a function** — called with the slot's props, returning vnodes. For a **scoped slot**, a string can declare the scope explicitly with `<template #row="scope">{{ scope.item.name }}</template>`; if you omit the wrapping `<template>`, Vue Test Utils exposes the slot scope as an object named `params`. ## attrs: everything the component does not declare `attrs` is for **fallthrough attributes**: `id`, `class`, `aria-*`, `data-*` or listeners the component does not list among its props or emits. By default Vue applies them to the component's single root element, so `wrapper.attributes('id')` reads them back. Mechanically, `mount()` spreads `attrs` and then `props` into one vnode props object. That has two consequences worth knowing: 1. When a key appears in both, the `props` value wins, because it is spread last; `wrapper.props()` shows it and `wrapper.attributes()` does not. 2. The option names record your intent, but the prop-or-attribute split is made by Vue from the component's declarations, exactly as in a real parent. ## Changing props after mount: setProps `wrapper.setProps({ title: 'Invoice #43' })` writes the new values into the reactive props object the wrapper parent renders from, so the component re-renders as if its parent had changed a binding. Keys you do not mention keep their current values — it merges, it does not replace. It returns a promise; await it before asserting on the DOM. `setProps` exists only on the wrapper `mount()` returned. A child wrapper from `findComponent()` gets its props from its real parent's render, which the test does not control, so calling `setProps` there throws `You can only use setProps on your mounted component`. To change a child's props, change whatever feeds them: the root's props, or an interaction that updates the parent's state. ```ts const wrapper = mount(InvoiceCard, { props: { title: 'Invoice #42', total: 120 }, slots: { footer: '<button>Pay now</button>' }, attrs: { 'data-test': 'card' } }) await wrapper.setProps({ total: 99 }) // title keeps 'Invoice #42' ``` ## Common mistakes - Passing a slot's content through `props` because the child renders it — slot content goes in `slots`. - Expecting a string slot to render as literal text: it is compiled as a template. - Calling `setProps` on a child wrapper and treating the thrown error as a Vue bug. - Forgetting that `setProps` merges, then asserting that an omitted prop fell back to its default.
- How do you fill a scoped slot when mounting with Vue Test Utils, and read the slot's scope?Give the slot a string such as `<template #row="scope">{{ scope.item.name }}</template>`, which names the scope explicitly. Without the wrapping `<template>`, Vue Test Utils exposes the scope as `params`, so `'{{ params.item.name }}'` works too. A function value is another option: it receives the slot props and returns vnodes built with `h()`.
- A child's rendering depends on a prop its parent passes; how do you exercise a new value without setProps on the child?Change the source of that prop. If the parent derives it from its own props, call `setProps` on the root wrapper; if it comes from parent state, drive the interaction that changes that state, such as `trigger('click')`. The child's props are its parent's render output, which is exactly what the test should be exercising.
saying these in an interview costs you the question
- Slot content should be passed as a prop because the child renders it.
- A string slot value is always rendered as escaped plain text.
- setProps replaces the whole props object, resetting keys you leave out.
- You can call setProps on any wrapper returned by findComponent().
- When a key is in both attrs and props, the attrs value wins.