Working an application in Vue.js with TDD — An extensive guide for people who have time — part 1
After all, teaching how to mount an application without tests is very easy
This is the first in a series of articles:
- Part 1: Setup and the first test
- Part 2: Continuing the UserView
- Part 3: Testing the store and the rest of the presentation components
- Part 4: Testing the API request service
- Part 5: Adding and testing with third-party dependencies
- Part 6: Overview— 26/11
If you want to read it in pt-BR, check it out here.
If you are curious, go directly to the repository with the final code:
In case you’ve decided do go over the full tutorial, let’s go! 🚀
During the VueJS Summit 2018 workshops I talked to Edd Yerburgh in person and showed him the way I was testing my applications, and after some encouragement, here I am, writing this article 😅
I hope to show how easy it is to test on the front, mainly how easy it is to test an application with Vue.js!
This tutorial has many images and a certain density of concepts, that’s why I recommend a calm reading in case you don’t have the tool’s base.
What we are going to do
The project will be very simple. We are going to make an application that communicates with the Github API, searching for a user from a typed username.
It may seem a small project but in it we are going to see many kinds of tests we find in a Vue.js application development. Here we are going to test:
- Components
- Vuex
- Services
In this project we are going to apply the TDD technique!
We are going to write a test that will fail and then make a minimum effort to make it pass. Finally, we are going to refactor it if necessary, repeating this until we finalize our application.
One of the advantages of this technique is that we end up having much faster feedbacks allowing us to work with much smaller cycles among the alterations during our project’s development.
It may seem difficult to apply the TDD in the beginning because it demands a mindset change, as we are writing tests before writing a production code.
I want to remind you here that we are not meant to strictly follow everything that is proposed, but for this project I want to show that this can be something simple to be done.
Modelling the application
First of all, let’s plan how we are going to make our application.
We are going to brake it into three components:
- UserView — “smart” component, responsible for making the communication with the store and loading our presentation components
- VUserSearchForm — “dumb” components, responsible for rendering the form and also for transmitting a message to the parent component with the research term
- VUserProfile — “dumb” component, responsible for rendering our researched user information
We are also going to count with a service to make requests for the Github API.
Download the vue-cli and let’s create our project:
npm i -g @vue/cli
vue create tdd-app
Below you have the features I’ve chosen. Choose the most convenient to you. I may cover E2E tests on a next article, who knows?
Let’s propose a challenge: we are only going to run the npm run serve
when we finish our application.
Let’s clean the project
A project through the Vue CLI ends up bringing some initial files. Let’s remove everything we don’t need in this first moment.
Remove:
- src/store.js
- src/assets/logo.png
- src/components/HelloWorld.vue
- src/views/About.vue
- src/views/Home.vue
- tests/unit/HelloWorld.spec.js
Clean App.vue
<template>
<div id="app">
<router-view/>
</div>
</template><style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
</style>
Clean router.js
import Vue from 'vue'
import Router from 'vue-router'Vue.use(Router)export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: []
})
Create a file to put in our store:
// src/store/index.jsimport Vue from 'vue'
import Vuex from 'vuex'
import state from '@/store/state'
import mutations from '@/store/mutations'
import actions from '@/store/actions'Vue.use(Vuex)export default new Vuex.Store({
state,
mutations,
actions,
})
We created the files: src/store/state.js
src/store/actions.js
src/store/mutations.js
and all of them have the same piece of code above:
export default {}
We added a npm script to observe our tests on package.json
..."test:unit": "vue-cli-service test:unit",
"test:unit:watch": "vue-cli-service test:unit --watchAll"
And finally, we are going to install some dependencies we are going to use in the project
npm i -d axios
npm i -D flush-promises nock
Creating our first test
Now we can start creating our tests. Let’s start by our first component, the UserView
, remembering that we are using jest as our tests’ framework.
This will be a smart component, responsible for carrying all the other screen components, and it will make the communication with the store to make the Github user’s search.
Let’s create then the file: tests/unit/UserView.spec.js
describe('UserView', () => {
it('works', () => {})
})
And we can run the tests: npm run test:unit
But what exactly do we test in our components?
We can test many things, but we can base ourselves in the following topics:
If the component renders
We need to guarantee that at least the component is rendering correctly.
If it renders the right thing
If we guarantee that the component renders, then we can test if it is rendering the right thing. Our component has a button and an input, or it contains a button and a comment saying “Input will be implemented in v2”.
Its binds
We tested all possible binds. If our component passing the right props to its son components?
The events
By clicking a button or receiving certain event, is our component behaving expectedly?
Extreme cases
In the case of a list, we need to guarantee how it will behave with an empty list, a list with 5 items, or 100 items.
Now, yes
By knowing this, we can finally make our test. Let’s see the first case, if our UserView component is rendered:
We are using jest as our test framework and the vue-test-utils to help operations with our components.
On line 7, we are making a shallowMount of our component. That means:
Let’s render only the first level of its dependencies
The vue-test-utils also provides another method called mount, in which we render the complete dependencies tree.
But the shallowMount is already enough for our case.
On line 10, we are taking the wrapper, which is a representation of our component, created by the vue-test-utils, and then we are “taking a picture” of our component’s html. This html exists thanks to the vue-test-utils.
By running the npm run test:unit
we have our first error
The component still doesn’t exist. Then we get to the TDD phase where we can finally create our file.
Here we have written a minimum representation of our component, and thanks to this, we have our first test passing, running then: npm run test:unit:watch
But what the hell is this snapshot, exactly?
Basically, jest took a “picture” of our component’s htm and if we check in the directories, a file __snapshots__
was created inside tests
Now we have a reference of our component and in case there is any kind of alteration on its html, our test will fail.
Make a test, change the UserView.vue
and see the test failing
Now we can see that, in case there is any alteration in our html, we need to correct the error or go back to the original status.
In case the alteration is intentional we can update the snapshot, saying that this version will be our new reference.
To do this, you only need to press u on the terminal and the snapshot will be updated automatically 😊. To see more options, you only need to click w on the terminal that a list of jest options will open.
NEVER touch on a snapshot file. This file is generated for you!
Overview
In this first article, we did:
- The introduction of what we will do
- Planning our application
- Initial setup of our project leaving only the minimum files to be worked
- Explanation of what is tested on the front
- Created our first test
Stay tuned to next week we will be deepening in the tests of our component and we will end up integrating with the store.
Thanks for the attention, if you liked, please click on the 💚, and any questions, suggestions or corrections feel free to send me a message, I thank you very much 😄.
My Twitter: @DKuroski
See you next week 😄