Skip to content
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
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/.vuepress/config.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
const sidebar = {
guide: [
{
title: 'Introduction',
collapsable: false,
children: ['/guide/introduction', '/guide/write-easy-test']
},
{
title: 'Essentials',
collapsable: false,
children: [
'/guide/installation',
'/guide/introduction',
'/guide/a-crash-course',
'/guide/conditional-rendering',
'/guide/event-handling',
Expand Down Expand Up @@ -71,7 +75,7 @@ module.exports = {
'/api/': sidebar.api
},
nav: [
{ text: 'Guide', link: '/guide/introduction' },
{ text: 'Guide', link: '/guide/installation' },
{ text: 'API Reference', link: '/api/' },
{ text: 'Migration from VTU 1', link: '/guide/migration' },
{ text: 'GitHub', link: 'https://github.com/vuejs/vue-test-utils-next' }
Expand Down
4 changes: 1 addition & 3 deletions src/guide/introduction.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## Vue Test Utils Documentation
# What is Vue Test Utils?

Welcome to Vue Test Utils, the official testing utility library for Vue.js!

Expand All @@ -9,8 +9,6 @@ In short:
* [Vue Test Utils 1](https://github.com/vuejs/vue-test-utils/) targets [Vue 2](https://github.com/vuejs/vue/).
* [Vue Test Utils 2](https://github.com/vuejs/vue-test-utils-next/) targets [Vue 3](https://github.com/vuejs/vue-next/).

## What is Vue Test Utils?

Vue Test Utils (VTU) is a set of utility functions aimed to simplify testing Vue.js components. It provides some methods to mount and interact with Vue components in an isolated manner.

Let's see an example:
Expand Down
125 changes: 125 additions & 0 deletions src/guide/write-easy-test.md
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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great, i love this


:::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.