-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add "Write components that are easy to test" #52
Closed
Closed
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
e1b0f55
Add introduction block
afontcu ca46654
Add draft
afontcu 8423679
Fix title
afontcu e15fa9b
Fix grammar
afontcu 4e6e2d5
Improve wording
afontcu 6b4837b
Add clarification
afontcu 386b47a
Add example
afontcu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
# Write components that are easy to test | ||
|
||
Vue Test Utils helps you write tests for Vue components. However, there's only so much VTU can do. | ||
|
||
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. | ||
|
||
The following list provide general guidance and it might come in handy in common scenarios. | ||
|
||
## Do not test implementation details | ||
|
||
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: | ||
|
||
| **Inputs** | Examples | | ||
| ------------ | ------------------------------------------------- | | ||
| Interactions | Clicking, typing... any "human" interaction | | ||
| Props | The arguments a component receives | | ||
| Data streams | Data incoming from API calls, data subscriptions… | | ||
|
||
| **Outputs** | Examples | | ||
| ------------ | ---------------------------------------------- | | ||
| DOM elements | Any _observable_ node rendered to the document | | ||
| Events | Emitted events (using `$emit`) | | ||
| Side Effects | Such as `console.log` or API calls | | ||
|
||
### Everything else is implementation details | ||
|
||
Notice how this list does not include elements such as internal methods, intermediate states or even data. | ||
|
||
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. | ||
|
||
For example, let's assume a basic Counter component that features a button to increment a counter. We could write the following test: | ||
|
||
```vue | ||
<template> | ||
<p class="paragraph">Times clicked: {{ count }}</p> | ||
<button @click="increment">increment</button> | ||
</template> | ||
|
||
<script> | ||
export default { | ||
data() { | ||
return { count: 0 } | ||
}, | ||
methods: { | ||
increment() { | ||
this.count++ | ||
} | ||
} | ||
} | ||
</script> | ||
``` | ||
|
||
We could write the following test: | ||
|
||
```js | ||
import { mount } from '@vue/test-utils' | ||
import Counter from './Counter.vue' | ||
|
||
test('counter text updates', async () => { | ||
const wrapper = mount(Counter) | ||
const paragraph = wrapper.find('.paragraph') | ||
|
||
expect(paragraph.text()).toBe('Times clicked: 0') | ||
|
||
await wrapper.setData({ count: 2 }) | ||
|
||
expect(paragraph.text()).toBe('Times clicked: 2') | ||
}) | ||
``` | ||
|
||
Notice how here we're updating its internal data, and we also rely on details (from a user perspective) such as CSS classes. | ||
|
||
:::tip | ||
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**. | ||
::: | ||
|
||
Instead, the following test tries to stick with the inputs and outputs listed above: | ||
|
||
```js | ||
import { mount } from '@vue/test-utils' | ||
|
||
test('text updates on clicking', async () => { | ||
const wrapper = mount(Counter) | ||
|
||
expect(wrapper.text()).toBe('Times clicked: 0') | ||
|
||
const button = wrapper.find('button') | ||
await button.trigger('click') | ||
await button.trigger('click') | ||
|
||
expect(wrapper.text()).toBe('Times clicked: 2') | ||
}) | ||
``` | ||
|
||
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. | ||
|
||
## Build smaller, simpler components | ||
|
||
A general rule of thumb is that if a component does less, then it will be easier to test. | ||
|
||
Making smaller components will make them more composable and easier to understand. Following is a list of suggestions to make components simpler. | ||
|
||
### Extract API calls | ||
|
||
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. | ||
|
||
:::tip | ||
Check out the [Making HTTP requests](../guide/http-requests.md) guide if you are unfamiliar with testing API calls. | ||
::: | ||
|
||
### Extract complex methods | ||
|
||
Sometimes a component might feature a complex method, perform heavy calculations, or use several dependencies. | ||
|
||
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. | ||
|
||
This has the additional benefit of ending up with a component that's easier to understand because complex logic is encapsulated in another file. | ||
|
||
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](../guide/http-requests.md) is a good example – axios is quite a complex library! | ||
|
||
## Write tests before writing the component | ||
|
||
You can't write untestable code if you write tests beforehand! | ||
|
||
Our [Crash Course](../guide/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. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great, i love this