Vue 3 with Jest and Vue Tests Utils: Introduction
Tags: vue3, jest
Published: 2021-08-22 05:17:49

Never took the time to create a vue project with unit test?
Before to begin, we will see what are the advantages to use unit test.

- It will increase your confidence ! Indeed, Tests also called Specs allow you to add new feature without breaking the existing code.
- It will be use by any developers concerned by the project, you can consider Specs like a documentation of the application.
- The quality of code you will produce only can improve

Let's start, we will begin with creating the project, it will take about 2 minutes.
```
vue create vue-3-test-unit && cd vue-3-test-unit
```
Here what features i selected: 
![Capture d’écran 2021-08-20 à 11.25.07](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wn6uzt13ccn9hoybidsp.png)
 
<h1>Select a pattern for your Tests</h1>
You will find lot of pattern to organize your tests. 

<h2>The pattern I recommend</h2>
I personally use the next pattern. The advantage is that all tests will be in the tests/unit folder.
```
tests/
├─ unit/
│  ├─ assets
│  ├─ components
│  ├─ js
│  ├─ router
│  ├─ store
│  ├─ views
│  ├─ App.test.js
```
To get this pattern, just reproduce it in your terminal:
```
mkdir tests/unit/assets
mkdir tests/unit/components
mkdir tests/unit/router
mkdir tests/unit/store
mkdir tests/unit/views
mv tests/unit/example.spec.js tests/unit/App.spec.js
``` 

<h2>Others kind of pattern</h2>
If you are interested, you will find below some others kind of pattern:
```
src/
├─ components/
│  ├─ navbar.js
│  ├─ navbar.style.js
│  └─ navbar.test.js
```
```
src/
├─ components/
├─ __tests__/
│  │  └─ navbar.test.js
│  └─ navbar.js
```

<h1>Tests/specs syntax</h1>
Let's try to next command:
```
npm run test:unit
```
As you can see below, it's showing the result of your test.
![Capture d’écran 2021-08-20 à 14.53.31](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1iw4bvplmux1v9z9sfnc.png)
Explanations:
- Test Suites is a kind of grouped test
- Tests show the number of test
- Snapshots is used to be sure an entire component runs like you want, it also used as an integration test
You can see "PASS" in green. If a test fail, you will get "FAIL" in red. 

<h2>Understand how a test works</h2>
Let's open the App.spec.js file so we can understand how it works. We got the below code that i will explain.
```js
import { shallowMount } from '@vue/test-utils'
import HelloWorld from '@/components/HelloWorld.vue'

describe('HelloWorld.vue', () => {
  it('renders props.msg when passed', () => {
    const msg = 'new message'
    const wrapper = shallowMount(HelloWorld, {
      props: { msg }
    })
    expect(wrapper.text()).toMatch(msg)
  })
})
```
<h3>Why must we do imports?</h3>
Because an vue application have to be mounted into a DOM element, you will need to mount some vue.file to make tests.
For example, if we want to perform a test on the HelloWorld.vue page, we have to import. Two solutions to do this:
<h4>mount</h4>
```js
import { mount } from '@vue/test-utils'
import HelloWorld from '@/components/HelloWorld.vue'
```
<h4>shallowMount</h4>
Like mount, it creates a Wrapper that contains the mounted and rendered Vue component, but it is possible to use it with stubbed child components.
```js
import { shallowMount } from '@vue/test-utils'
import HelloWorld from '@/components/HelloWorld.vue'
```

<h3>Syntax of a test explained</h3>
To write a test, you only need to know how to do :
- a test suite : use 'describe' to do it
- a test : use 'test' or 'it'
- a goal for your test : 'expect' allow you to define what are the expectation of the test

<h4>Describe</h4>
The description must be clear, we use the name of the component or the function name tested.
If you want to use ES6
```js
describe('HelloWorld.vue', () => {

})
```
Without ES6:
```js
describe('HelloWorld.vue', function()  {

})
```
<h4>Test with expect</h4>
After putting the description of this test, we prepare variable that will be used in the expect.
```js
  it('renders props.msg when passed', () => {
    const msg = 'new message'
    const wrapper = shallowMount(HelloWorld, {
      props: { msg }
    })
    expect(wrapper.text()).toMatch(msg)
  })
```
This test means that the DOM of the HelloWorld.vue page must have a text containing 'new message' by the props 'msg'. 

<h3>Write our first test</h3>
At this point, you can manage your project in several ways.
- Code first, test after : You can code the component first and add the test. 
- Test first, code after : You can add the test first, so you can begin the code with a clear idea of what you will do. To go further, check for the TDD (test driven development). I will use this approach.

<h4>Begin with the test</h4>
First, we must think about the feature we want to implement. In this case, why not to begin with adding a navbar to the App? The process will be :
- create the test of navbar component's
- create the navbar component's
- add it in App.vue

<h5>Write the test</h5>
The test will be simple, we will erase the content of App.spec.js and add a test that will check if the navbar component's exist in App.vue
```js
import { shallowMount } from '@vue/test-utils'
import Navbar from '../../src/components/Navbar.vue'
import App from '../../src/App.vue'
import router from '../../src/router/index.js'

describe('App.vue', () => {
  it('Navbar component\'s exist', async () => {
    router.push('/')
    await router.isReady()
    const wrapper = shallowMount(App, {
      global: {
        plugins: [router]
      }
    })
    expect(wrapper.findComponent(Navbar).exists()).toBe(true)
  })
})

```

<h5>Launch the test</h5>
The test will fail, the import doesn't exist so you will get this error.
![Capture d’écran 2021-08-20 à 19.08.17](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d7svhvssf659pdo35nmx.png)
 
<h5>Create the component</h5>
The last error told us the problem, we have to create the component! 
```js
touch src/components/Navbar.vue
```
Just add the below code if you don't want to make a Navbar by yourself.
```js
<template>
  <div id="nav">
    <router-link to="/">Home</router-link> |
    <router-link to="/about">About</router-link>
  </div>
</template>

<script>
export default {
  name: 'Navbar',
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

#nav {
  padding: 30px;
}

#nav a {
  font-weight: bold;
  color: #2c3e50;
}

#nav a.router-link-exact-active {
  color: #42b983;
}
</style>

```

<h5>Add the component to App.vue</h5>
We return in the App.vue page to add the Navbar. If you don't want to add the import part here, you can add it in your main.js
```js
<template>
  <navbar />
  <router-view />
</template>

<script>
import Navbar from '@/components/Navbar.vue'

export default {
  name: 'App',
  components: {
    Navbar
  },
}
</script>
```
<h5>Launch the test</h5>
```
npm run test:unit            
```
![Capture d’écran 2021-08-20 à 23.58.55](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s2lw6unbjl0x6veafrq4.png)
It works ! Some other kind of test will be added in another post ;)