Skip to content

Commit 6ffa638

Browse files
authored
Merge pull request #38 from vuejs/docs/stubs-shallow
docs: shallow and stubs
2 parents 464a04c + f37ea42 commit 6ffa638

File tree

1 file changed

+197
-1
lines changed

1 file changed

+197
-1
lines changed

src/guide/stubs-shallow-mount.md

+197-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,199 @@
11
# Stubs and Shallow Mount
22

3-
Mostly `mount({ shallow: true })` and `global.stubs`.
3+
Vue Test Utils provides some advanced features for *stubbing* components. A *stub* is where you replace an existing implementation of a custom component with a dummy component that doesn't do anything at all, which can simplify an otherwise complex test. Let's see an example.
4+
5+
## Stubbing a single child component
6+
7+
A common example is when you would like to test something in a component that appers very high in the component hierarchy.
8+
9+
In this example, we have an `<App>` that renders a message, as well as a `FetchDataFromApi` component that makes an API call.
10+
11+
```js
12+
const FetchDataFromApi = {
13+
name: 'FetchDataFromApi',
14+
template: `
15+
<div>{{ result }}</div>
16+
`,
17+
async mounted() {
18+
const res = await axios.get('/api/info')
19+
this.result = res.data
20+
},
21+
data() {
22+
return {
23+
result: ''
24+
}
25+
}
26+
}
27+
28+
const App = {
29+
components: {
30+
FetchDataFromApi
31+
},
32+
template: `
33+
<div>
34+
<h1>Welcome to Vue.js 3</h1>
35+
<fetch-data-from-api />
36+
</div>
37+
`
38+
}
39+
```
40+
41+
We do not want to make the API call in this particular test, we just want to assert the message is rendered. In this case, we could use the `stubs`, which appears in the `global` mounting option.
42+
43+
```js {12}
44+
test('stubs', () => {
45+
const wrapper = mount(App, {
46+
global: {
47+
stubs: {
48+
FetchDataFromApi: {
49+
template: '<span />'
50+
}
51+
}
52+
}
53+
})
54+
55+
console.log(wrapper.html()) // <div><h1>Welcome to Vue.js 3</h1><span></span></div>
56+
expect(wrapper.html()).toContain('Welcome to Vue.js 3')
57+
})
58+
```
59+
60+
Notice that the template is showing `<span></span>` where `<fetch-data-from-api />` was? We replaced it with a stub - in this case, we provided our own implementation by passing in a `template`.
61+
62+
You can also get a default stub, instead of providing your own:
63+
64+
```js {9}
65+
test('stubs', () => {
66+
const wrapper = mount(App, {
67+
global: {
68+
stubs: {
69+
FetchDataFromApi: true
70+
}
71+
}
72+
})
73+
console.log(wrapper.html()) // <div><h1>Welcome to Vue.js 3</h1><fetch-data-from-api-stub></fetch-data-from-api-stub></div>
74+
expect(wrapper.html()).toContain('Welcome to Vue.js 3')
75+
})
76+
```
77+
78+
This will stub out *all* the `<FetchDataFromApi />` components in the entire render tree, regardless of what level they appear at. That's why it is in the `global` mounting option.
79+
80+
## Stubbing all children components
81+
82+
Sometimes you might want to stub out *all* the custom components. For example you might have a component like this:
83+
84+
```js
85+
const ComplexComponent = {
86+
components: { ComplexA, ComplexB, ComplexC },
87+
template: `
88+
<div>
89+
<h1>Welcome to Vue.js 3</h1>
90+
<ComplexA />
91+
<ComplexB />
92+
<ComplexC />
93+
</div>
94+
`
95+
}
96+
```
97+
98+
Imagine each of the `<Complex>` does something complicated, and you are only interested in testing that the `<h1>` is rendering the correct greeting. You could do something like:
99+
100+
```js
101+
const wrapper = mount(ComplexComponent, {
102+
global: {
103+
stubs: {
104+
ComplexA: true,
105+
ComplexB: true,
106+
ComplexC: true,
107+
}
108+
}
109+
})
110+
```
111+
112+
But that's a lot of boilerplate. VTU has a `shallow` mounting option that will automatically stub out all the child components:
113+
114+
```js {5}
115+
test('shallow', () => {
116+
const wrapper = monut(ComplexComponent, {
117+
shallow: true
118+
})
119+
console.log(wrapper.html()) // <div><h1>Welcome to Vue.js 3</h1><complex-a-stub></complex-a-stub><complex-b-stub></complex-b-stub><complex-c-stub></complex-c-stub></div>
120+
})
121+
```
122+
123+
> TIP: If you used VTU V1, you may remember this as `shallowMount`. That method is still available, too - it's the same as writing `shallow: true`.
124+
125+
## Default Slots and `shallow`
126+
127+
Since `shallow` stubs out all the content of a components, any `<slot>` won't get rendered when using `shallow`. While this is not a problem in most cases, there are some scenarios where this isn't ideal.
128+
129+
```js
130+
const CustomButton = {
131+
template: `
132+
<button>
133+
<slot />
134+
</button>
135+
`
136+
}
137+
```
138+
139+
And you might use it like this:
140+
141+
```js
142+
const App = {
143+
props: ['authenticated'],
144+
components: { CustomButton },
145+
template: `
146+
<custom-button>
147+
<div v-if="authenticated">Log out</div>
148+
<div v-else>Log in</div>
149+
</custom-button>
150+
`
151+
}
152+
```
153+
154+
If you are using `shallow`, the `<slot />` will not be rendered, since the render function in `<custom-button />` is stubbed out. That means you won't be able to verify the correct text is rendered! For this use case, you can use `config.renderDefaultStub`, which will render the default `<slot />`, even when using `shallow`:
155+
156+
```js {1,4,8}
157+
import { config, mount } from '@vue/test-utils'
158+
159+
beforeAll(() => {
160+
config.renderStubDefaultSlot = true
161+
})
162+
163+
afterAll(() => {
164+
config.renderStubDefaultSlot = false
165+
})
166+
167+
test('shallow with stubs', () => {
168+
const wrapper = mount(AnotherApp, {
169+
props: {
170+
authenticated: true
171+
},
172+
shallow: true
173+
})
174+
175+
expect(wrapper.html()).toContain('Log out')
176+
})
177+
```
178+
179+
Since this behavior is global, not on a `mount` by `mount` basis, you need to remember to enable/disable it before and after each test.
180+
181+
:::: tip If you prefer this behavior, you can enable this globally by importing `config` in your test setup file, and setting `renderStubDefaultSlot` to `true`. Unfortunately, due to technical limitations, this behavior is not extended to slots other than the default slot.
182+
183+
## `mount`, `shallow` and `stubs`: which one and when?
184+
185+
As a rule of thumb, the more your tests resemble the way your software is used, the more confidence they can give you.
186+
187+
Tests that use `mount` will render the entire component hierarchy, which is closer to what the user will experience in a real browser.
188+
189+
On the other hand, tests using `shallow` are focused on a specific component. `shallow` can be useful for testing advanced components in complete isolation. If you just have one or two components that are not relevant to your tests, consider using `mount` in combination with `stubs` instead of `shallow`. The more you stub, the less production-like your test becomes.
190+
191+
Keep in mind that whether you are doing a full mount or a shallow render, good tests focus on inputs (`props` and user interaction, such as with `trigger`) and outputs (the DOM elements that are rendered, and events), not implementation details.
192+
193+
So regardless of which mounting method you choose, we suggest keeping these guidelines in mind.
194+
195+
## Conclusion
196+
197+
- use `global.stubs` to replace a component with a dummy one to simplify your tests
198+
- use `shallow: true` (or `shallowMount`) to stub out all child components
199+
- use `config.stubRenderDefaultSlot` to render the default `<slot>` for a stubbed component

0 commit comments

Comments
 (0)