Skip to content

Commit 7e1df9f

Browse files
committed
Add page
1 parent c311c09 commit 7e1df9f

File tree

1 file changed

+125
-0
lines changed

1 file changed

+125
-0
lines changed

docs/guide/essentials/easy-to-test.md

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Write components that are easy to test
2+
3+
Vue Test Utils helps you write tests for Vue components. However, there's only so much VTU can do.
4+
5+
Following is a list of suggestions to write code that is easier to test, and to write tests that are meaningful and simple to maintain.
6+
7+
The following list provide general guidance and it might come in handy in common scenarios.
8+
9+
## Do not test implementation details
10+
11+
Think in terms of inputs and outputs from a user perspective. Roughly, this is everything you should take into account when writing a test for a Vue component:
12+
13+
| **Inputs** | Examples |
14+
| ------------ | ------------------------------------------------- |
15+
| Interactions | Clicking, typing... any "human" interaction |
16+
| Props | The arguments a component receives |
17+
| Data streams | Data incoming from API calls, data subscriptions… |
18+
19+
| **Outputs** | Examples |
20+
| ------------ | ---------------------------------------------- |
21+
| DOM elements | Any _observable_ node rendered to the document |
22+
| Events | Emitted events (using `$emit`) |
23+
| Side Effects | Such as `console.log` or API calls |
24+
25+
### Everything else is implementation details
26+
27+
Notice how this list does not include elements such as internal methods, intermediate states or even data.
28+
29+
The rule of thumb is that **a test should not break on a refactor**, that is, when we change its internal implementation without changing its behavior. If that happens, the test might rely on implementation details.
30+
31+
For example, let's assume a basic Counter component that features a button to increment a counter. We could write the following test:
32+
33+
```vue
34+
<template>
35+
<p class="paragraph">Times clicked: {{ count }}</p>
36+
<button @click="increment">increment</button>
37+
</template>
38+
39+
<script>
40+
export default {
41+
data() {
42+
return { count: 0 }
43+
},
44+
methods: {
45+
increment() {
46+
this.count++
47+
}
48+
}
49+
}
50+
</script>
51+
```
52+
53+
We could write the following test:
54+
55+
```js
56+
import { mount } from '@vue/test-utils'
57+
import Counter from './Counter.vue'
58+
59+
test('counter text updates', async () => {
60+
const wrapper = mount(Counter)
61+
const paragraph = wrapper.find('.paragraph')
62+
63+
expect(paragraph.text()).toBe('Times clicked: 0')
64+
65+
await wrapper.setData({ count: 2 })
66+
67+
expect(paragraph.text()).toBe('Times clicked: 2')
68+
})
69+
```
70+
71+
Notice how here we're updating its internal data, and we also rely on details (from a user perspective) such as CSS classes.
72+
73+
:::tip
74+
Notice that changing either the data or the CSS class name would make the test fail. The component would still work as expected, though. This is known as a **false positive**.
75+
:::
76+
77+
Instead, the following test tries to stick with the inputs and outputs listed above:
78+
79+
```js
80+
import { mount } from '@vue/test-utils'
81+
82+
test('text updates on clicking', async () => {
83+
const wrapper = mount(Counter)
84+
85+
expect(wrapper.text()).toBe('Times clicked: 0')
86+
87+
const button = wrapper.find('button')
88+
await button.trigger('click')
89+
await button.trigger('click')
90+
91+
expect(wrapper.text()).toBe('Times clicked: 2')
92+
})
93+
```
94+
95+
Libraries such as [Vue Testing Library](https://github.com/testing-library/vue-testing-library/) are build upon these principles. If you are interested in this approach, make sure you check it out.
96+
97+
## Build smaller, simpler components
98+
99+
A general rule of thumb is that if a component does less, then it will be easier to test.
100+
101+
Making smaller components will make them more composable and easier to understand. Following is a list of suggestions to make components simpler.
102+
103+
### Extract API calls
104+
105+
Usually, you will perform several HTTP requests throughout your application. From a testing perspective, HTTP requests provide inputs to the component, and a component can also send HTTP requests.
106+
107+
:::tip
108+
Check out the [Making HTTP requests](../advanced/http-requests.md) guide if you are unfamiliar with testing API calls.
109+
:::
110+
111+
### Extract complex methods
112+
113+
Sometimes a component might feature a complex method, perform heavy calculations, or use several dependencies.
114+
115+
The suggestion here is to **extract this method and import it to the component**. This way, you can test the method in isolation using Jest or any other test runner.
116+
117+
This has the additional benefit of ending up with a component that's easier to understand because complex logic is encapsulated in another file.
118+
119+
Also, if the complex method is hard to set up or slow, you might want to mock it to make the test simpler and faster. Examples on [making HTTP requests](../advanced/http-requests.md) is a good example – axios is quite a complex library!
120+
121+
## Write tests before writing the component
122+
123+
You can't write untestable code if you write tests beforehand!
124+
125+
Our [Crash Course](../essentials/a-crash-course.md) offers an example of how writing tests before code leads to testable components. It also helps you detect and test edge cases.

0 commit comments

Comments
 (0)