Kicking off from A guide to better predictable code, we create our project boilerplate; assuming you have the vue-cli already installed. Right on, our awesome-todo
vue create awesome-todo
We manually select our project setup. Using Vue-2, allowing the router
, vuex
as well as unit testing
along with the defaults selected.
Select jest
when it comes to our unit testing solution and store the configuration in your package.json file. Are you ready? Good, let's get on to the next step.
For purposes of this guide, we'll use bootstrap; particularly, bootstrap-vue. Let's shorten those CSS classes.
npm install bootstrap-vue
In your main.js
file, add the necessary configurations.
import { BootstrapVue, BootstrapVueIcons } from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
Vue.use(BootstrapVue)
Vue.use(BootstrapVueIcons)
Spot on!
On the home/ landing page of our application, we want a welcome message displayed. For our case;
' Welcome to TheGreenCodes awesome to-do list. '
To begin, though, we test. As I said, we think before we implement. We think of the feature we want to implement before actually implementing it. We then write tests for this feature, to avoid the addiction cheat trap where we say we'll write a test after then never actually get to it.
Create a file home.spec.js
under the unit directory in the tests folder.
Notice how we name the file. Our test runner will look for javascript files with the spec keyword, through the project, under this directory. Now copy the following code snippet below.
import { shallowMount } from '@vue/test-utils'
import Home from '@/views/Home.vue'
// what componet are the tests referring to
describe('Home.vue', () => {
// what feature or spec are we targetting specifically
it('Displays a welcome message', () => {
const welcomeMessage = 'Welcome to TheGreenCodes awesome to-do list'
const wrapper = shallowMount(Home)
expect(wrapper.text()).toMatch(welcomeMessage)
})
})
To run this as well as consecutive tests:
npm run test: unit
Our test fails; rather horribly. Reading the shell, you see:
expect(received).toMatch(expected)
Expected substring: "Welcome to TheGreenCodes awesome to-do list"
Received string: ""
We gave it a variable with the message to expect in the component Home
. What we know for certain, however, is that we haven't even touched that component.; hence the failure. Head over the Home component under views, remove the HelloWorld import and use and add an h2
tag with our welcome message. Re-run this test and see the difference.
Before we get any further, we should explain what the elements in our Home test mean.
As we have made use of comments, we shall describe target areas:
const wrapper = shallowMount(Home)
We create a variable , wrapper
, that holds our component. How we do this you ask? We import shallowMount
from Vue test utils. Just like the default component life-cycle hooks, our component is initialized, only this time, since we specified we wanted a shallow mount, any child component within this parent component is not included.
We then ask the question: 'Hey! Is there any mention of our title from within this component?' To which the suite complies with a yes or no depending on what we have. We expect this component to have text, not only text but that which matches our welcome message.
Behold! We have done the foundation building block of a test; tests must fail, tests must pass and the code must be refactored.
We break this statement down:
Tests must fail Our feature has not been implemented yet, so why on earth would we expect a test to pass?
Test must pass
Hey Marv, I wrote down that cool little feature. What next? Simple; the test must pass. The test whose feature we just wrote should pass.
Code must be refactored
When the same piece of code is edited later, does the code still pass? Can this component or code be edited and still let the test pass gracefully?
Do we get the 'It broke everything else!' exclamation?
Tests must fail, tests must pass and the code must be refactored.
We could go further with this test and specify what element we wanted to have the title:
const titleFound = wrapper.find('h2')
expect(titleFound.text()).toMatch(welcomeMessage)
You get the same result. Ignore the warning for UI element registration, incase you are already using it within your code, for now, we'll fix this in a while.
Let's not just make our application, but let's make a great UI. Adjust your code to look as below:
Home component
<template>
<div class="home">
<h2>Welcome to TheGreenCodes awesome to-do list</h2>
<div class="container">
<b-card class="list-container">
</b-card>
</div>
</div>
</template>
<script>
export default {
name: 'Home',
components: {
}
</script>
<style scoped>
.list-container{
max-width: 170%;
}
</style>
Refactor the routes: (This would mean another component under views with the name Completed
)
{
path: '/completed',
name: 'Completed',
component: () => import(/* webpackChunkName: "about" */ '../views/Completed.vue')
}
Our application entry component would also have a link to the completed. section, either as a replacement to the about page or an add-on
<router-link to="/completed">Completed</router-link>
In the end, we should have something similar to this:
We have a basic layout up, and a first taste of what testing involves. To dive or not to dive is the question I pose to you.
Hush for a moment and let it settle. We will proceed with our application in an upcoming article.
Be sure to check the code, if need be, from TheGreenCodes repo. Specifically, the project tag awesome-todo v0.1.0
.
Stick around for a while as we delve more into the internals; and yes, we can continue this conversation on tech twitter, where Potato, oh, my bad, Larry and I, Marvin, talk everything between code and smelling flowers over the weekends.
Peace out.
A small laugh, in case Lewis, finds himself here again:
'Kuingia kimahuru'