From e1b0f55f1fa27de8f6ad5d1c1602aa8aef0944cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Fontcuberta?= Date: Sat, 12 Sep 2020 09:33:17 +0200 Subject: [PATCH 1/7] Add introduction block --- src/.vuepress/config.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/.vuepress/config.js b/src/.vuepress/config.js index 49005c9..7b6dbd9 100644 --- a/src/.vuepress/config.js +++ b/src/.vuepress/config.js @@ -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', @@ -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' } From ca466544da3996ad079c6070d2235b1fa6b9c9f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Fontcuberta?= Date: Sat, 12 Sep 2020 09:33:26 +0200 Subject: [PATCH 2/7] Add draft --- src/guide/introduction.md | 4 +-- src/guide/write-easy-test.md | 60 ++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 src/guide/write-easy-test.md diff --git a/src/guide/introduction.md b/src/guide/introduction.md index 135ff5e..dc01693 100644 --- a/src/guide/introduction.md +++ b/src/guide/introduction.md @@ -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! @@ -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: diff --git a/src/guide/write-easy-test.md b/src/guide/write-easy-test.md new file mode 100644 index 0000000..7d95310 --- /dev/null +++ b/src/guide/write-easy-test.md @@ -0,0 +1,60 @@ +# Write code that is 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 easier to maintain. + +The following list provide general guidance. It might come in handy in common scenarios. + +## Do not test implementation details + +Think in terms of inputs and outputs. Roughly, this is everything you should take into account when writing a test for a Vue component: + +### Inputs + +**Props**: the prop values provided. + +**User inputs**: user interactions such as clicking, scrolling, typing… + +**Data streams**: data incoming from API calls, data subscriptions… + +### Outputs + +**DOM elements**: anything the component renders to the DOM. + +**events**: emitted (through `$emit`) events. + +**side effects**: any other *observable* side effect, such as `console.log`, cookie creation, API calls… + +## Everything else are implementation details + +Notice how this list does not include elements such as internal methods, or intermediate states or 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 public API. If that happens, it might mean the test relies on implementation details. + + +## Build smaller components + +If a component does less, then it will be easier to test. + + +### Extract API calls + +:::tip + Check out the [Making HTTP requests](../guide/http-requests.md) guide if you are unfamiliar with testing 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. + + +### Extract complex methods + +Sometimes a component might feature a complex method, with heavy calculations or 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 (if necessary), and you can mock the import when testing your component. + +## Write tests before components + +There's no way you write untestable code if you write tests before! + +In out [Crash Course](../guide/a-crash-course.md) you can see how writing tests before code leads to testable components, and also helps you test edge cases. \ No newline at end of file From 842367978872b86aa0a8ff88226ee44769335a00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Fontcuberta?= Date: Sat, 12 Sep 2020 09:34:30 +0200 Subject: [PATCH 3/7] Fix title --- src/guide/write-easy-test.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/guide/write-easy-test.md b/src/guide/write-easy-test.md index 7d95310..f90cfc4 100644 --- a/src/guide/write-easy-test.md +++ b/src/guide/write-easy-test.md @@ -1,4 +1,4 @@ -# Write code that is easy to test +# 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. From e15fa9b3a8aa539370a373a52b0d7c19475d5829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Fontcuberta?= Date: Mon, 7 Dec 2020 15:37:55 +0100 Subject: [PATCH 4/7] Fix grammar --- src/guide/write-easy-test.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/guide/write-easy-test.md b/src/guide/write-easy-test.md index f90cfc4..eabc833 100644 --- a/src/guide/write-easy-test.md +++ b/src/guide/write-easy-test.md @@ -26,7 +26,7 @@ Think in terms of inputs and outputs. Roughly, this is everything you should tak **side effects**: any other *observable* side effect, such as `console.log`, cookie creation, API calls… -## Everything else are implementation details +## Everything else is implementation details Notice how this list does not include elements such as internal methods, or intermediate states or data. From 4e6e2d53b23c2518e634fb21f2ce4c632274d9f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Fontcuberta?= Date: Mon, 7 Dec 2020 15:48:09 +0100 Subject: [PATCH 5/7] Improve wording --- src/guide/write-easy-test.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/guide/write-easy-test.md b/src/guide/write-easy-test.md index eabc833..236abb6 100644 --- a/src/guide/write-easy-test.md +++ b/src/guide/write-easy-test.md @@ -2,7 +2,7 @@ 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 easier to maintain. +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. It might come in handy in common scenarios. @@ -12,7 +12,7 @@ Think in terms of inputs and outputs. Roughly, this is everything you should tak ### Inputs -**Props**: the prop values provided. +**Props**: the provided prop values. **User inputs**: user interactions such as clicking, scrolling, typing… @@ -22,11 +22,11 @@ Think in terms of inputs and outputs. Roughly, this is everything you should tak **DOM elements**: anything the component renders to the DOM. -**events**: emitted (through `$emit`) events. +**events**: emitted events (using `$emit`). -**side effects**: any other *observable* side effect, such as `console.log`, cookie creation, API calls… +**side effects**: any other *observable* side effect, such as `console.log`, cookies, API calls… -## Everything else is implementation details +### Everything else is implementation details Notice how this list does not include elements such as internal methods, or intermediate states or data. @@ -35,7 +35,9 @@ The rule of thumb is that **a test should not break on a refactor**, that is, wh ## Build smaller components -If a component does less, then it will be easier to test. +A general rule of thumb is that if a component does less, then it will be easier to test. + +That being said ### Extract API calls @@ -53,8 +55,8 @@ Sometimes a component might feature a complex method, with heavy calculations or The suggestion here is to extract this method and import it to the component. This way, you can test the method in isolation (if necessary), and you can mock the import when testing your component. -## Write tests before components +## Write tests before writing the component There's no way you write untestable code if you write tests before! -In out [Crash Course](../guide/a-crash-course.md) you can see how writing tests before code leads to testable components, and also helps you test edge cases. \ No newline at end of file +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. \ No newline at end of file From 6b4837b5c16ec527daaeb9af63582ba23970e377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Fontcuberta?= Date: Mon, 7 Dec 2020 15:51:41 +0100 Subject: [PATCH 6/7] Add clarification --- src/guide/write-easy-test.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/guide/write-easy-test.md b/src/guide/write-easy-test.md index 236abb6..8a4b03c 100644 --- a/src/guide/write-easy-test.md +++ b/src/guide/write-easy-test.md @@ -28,9 +28,9 @@ Think in terms of inputs and outputs. Roughly, this is everything you should tak ### Everything else is implementation details -Notice how this list does not include elements such as internal methods, or intermediate states or data. +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 public API. If that happens, it might mean the test relies on implementation details. +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 (that is, without changing its public API). If that happens, it might mean the test relies on implementation details. ## Build smaller components From 386b47a09d18c0a6d54d5d0454b51a05a6473b83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Fontcuberta?= Date: Sat, 16 Jan 2021 19:02:55 +0100 Subject: [PATCH 7/7] Add example --- src/guide/write-easy-test.md | 109 +++++++++++++++++++++++++++-------- 1 file changed, 86 insertions(+), 23 deletions(-) diff --git a/src/guide/write-easy-test.md b/src/guide/write-easy-test.md index 8a4b03c..c66ca63 100644 --- a/src/guide/write-easy-test.md +++ b/src/guide/write-easy-test.md @@ -4,59 +4,122 @@ Vue Test Utils helps you write tests for Vue components. However, there's only s 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. It might come in handy in common scenarios. +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. Roughly, this is everything you should take into account when writing a test for a Vue component: +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 +| **Inputs** | Examples | +| ------------ | ------------------------------------------------- | +| Interactions | Clicking, typing... any "human" interaction | +| Props | The arguments a component receives | +| Data streams | Data incoming from API calls, data subscriptions… | -**Props**: the provided prop values. +| **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 | -**User inputs**: user interactions such as clicking, scrolling, typing… +### Everything else is implementation details -**Data streams**: data incoming from API calls, data subscriptions… +Notice how this list does not include elements such as internal methods, intermediate states or even data. -### Outputs +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. -**DOM elements**: anything the component renders to the DOM. +For example, let's assume a basic Counter component that features a button to increment a counter. We could write the following test: -**events**: emitted events (using `$emit`). +```vue + -**side effects**: any other *observable* side effect, such as `console.log`, cookies, API calls… + +``` -### Everything else is implementation details +We could write the following test: -Notice how this list does not include elements such as internal methods, intermediate states or even data. +```js +import { mount } from '@vue/test-utils' +import Counter from './Counter.vue' -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 (that is, without changing its public API). If that happens, it might mean the test relies on implementation details. +test('counter text updates', async () => { + const wrapper = mount(Counter) + const paragraph = wrapper.find('.paragraph') + expect(paragraph.text()).toBe('Times clicked: 0') -## Build smaller components + await wrapper.setData({ count: 2 }) -A general rule of thumb is that if a component does less, then it will be easier to test. + 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) -That being said + 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. +Check out the [Making HTTP requests](../guide/http-requests.md) guide if you are unfamiliar with testing 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. +### Extract complex methods +Sometimes a component might feature a complex method, perform heavy calculations, or use several dependencies. -### Extract complex methods +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. -Sometimes a component might feature a complex method, with heavy calculations or several dependencies. +This has the additional benefit of ending up with a component that's easier to understand because complex logic is encapsulated in another file. -The suggestion here is to extract this method and import it to the component. This way, you can test the method in isolation (if necessary), and you can mock the import when testing your component. +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 -There's no way you write untestable code if you write tests before! +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. \ No newline at end of file +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.