Testing is crucial for ensuring that our code is working as expected and maintaining the stability of the application. When working with Vue.js or any other js framework, Jest is an excellent choice for testing your components.
In this blog post, we will see how to set up your environment, write tests for different types of components, and share best practices for writing clean and maintainable tests using Jest and Vue 3.
By the end, you will have a solid understanding of how to test your Vue components using Jest.
Component testing is crucial for ensuring that individual components in your application are working as expected, and for maintaining the overall stability of your application.
It allows you to isolate and test specific functionality, catch bugs early, and ensure that changes to one component don’t break other application parts.
Additionally, component testing also helps in improving the maintainability of your code by providing clear documentation on how a component should behave and be used.
A Vue.js component typically includes the following to be tested:
However, not all components will require testing all of the above-listed aspects. It really depends on the complexity and the requirement of the component.
shallowMount
which we will use to create a shallow wrapper of the component, and find
which we will use to locate elements in the component's template.Before we can start writing tests for our Vue components, we need to set up our environment. This involves installing and configuring jest
, @vue/test-utils
and babel-jest
in our Vue project.
1. First, we need to install Jest and @vue/test-utils using npm or yarn:
npm install --save-dev jest @vue/test-utils
or
yarn add -D jest @vue/test-utils
<!-- HelloWorld.vue -->
<template>
<p>Hello there!</p>
</template>
Now, create a directory and name it as tests
. However, It’s not compulsory you can name it what you like.
I have added a
tests
directory inside the root directory, feel free to vary the path if you wish to.
helloworld.test.js
.// helloworld.test.js
import HelloWorld from "../src/components/HelloWorld.vue";
import { mount } from "@vue/test-utils";
test("HelloWorld Component renders the correct text", () => {
const wrapper = mount(HelloWorld);
expect(wrapper.text()).toBe("Hello there!");
});
If you’ve configured eslint in your project and it shows the error like test is not defined, no need to worry about just add the path of your
tests
directory inside .eslintignore file. The error should be disappeared!
Here, mount
can be replaced with shallowMount
according to requirements. wrapper
is the Vue instance involved in testing the component.
mount()
and shallowMount()
are both functions provided by the Vue Test Utils library for creating a test-specific Vue instance of a component.mount
is used when you need to test the component in the context of its child components and shallowMount
is used when you only need to test the component in isolation. However, shallowMount
makes the test faster."test" : "jest"
command to the script section of the package.json file and execute it like npm test
.Alas! the test is failing 😨.
It’s because Jest runs tests in a Node.js environment, which does not support the use of the import
statement by default. Instead, you should use the require
statement to import modules.
To fix this error, you can use a tool like Babel to transpile your JavaScript code, which will convert the import
statements into the equivalent require()
statements that Node.js can understand.
Moreover, we will go with another solution. That’s the usage of babel-jest
package to internally transpile import statements into required ones.
babel-jest
package, that will be used by jest to transpile our code.npm install babel-jest --save
babel-jest
by creating thejest.config.js
file as below.// jest.config.js
module.exports = {
transform: {
"^.+\\.jsx?$": "babel-jest",
},
};
Again running the test and it still gives the same error😞
@babel/preset-env
and configure it inside the .babelrc file in the root directory.npm install @babel/preset-env --save// .babelrc
{
"presets": ["@babel/preset-env"]
}
Now it gives a different error as below.
You see, our template has only HTML code and it’s being mounted here now. Let’s solve the error without any more ado.
The reason behind the above error is, jest is unable to understand and process .vue files!
As a solution, we need to set up the vue3-jest
package, which will work as a transformer for .vue files.
vue3-jest
packageWARNING: If you’re using Vue.js 2, user vue-jest.
npm install @vue/vue3-jest --save
// jest.config.js
module.exports = {
transform: {
"^.+\\.jsx?$": "babel-jest",
'^.+\\.vue$': '@vue/vue3-jest',
},
};
Again giving a new error, let’s explore it.
This error message indicates that the document
object is not defined in the context of the code that is currently being executed.
A web browser typically provides this object and is not available in a Node.js environment when running tests with Jest. Therefore, we need to specify the test document object.
Install jest-environment-jsdom
package and add it inside the jest.config.js.
npm install jest-environment-jsdom --save// jest.config.js
module.exports = {
transform: {
"^.+\\.jsx?$": "babel-jest",
'^.+\\.vue$': '@vue/vue3-jest',
},
testEnvironment: "jsdom",
};
Oops!! it’s giving another error now 😟
It’s being thrown in the latest version of @vue/test-utils package.
As a solution, update the jest.config.js file as below.
module.exports = {
transform: {
"^.+\\.vue$": "@vue/vue3-jest",
"^.+\\.jsx?$": "babel-jest",
},
testEnvironment: "jsdom",
testEnvironmentOptions: {
customExportConditions: ["node", "node-addons"],
},
};
Boom!! when you run the test it passes 👯
In the previous section, we looked at how to write a simple test for a Vue component.
However, a component can have different types of functionality such as data, computed properties, methods, etc.
In this section, we will look at how to test them using Jest and @vue/test-utils.
Consider a component named FruitList.vue for testing different aspects.
<!-- FruitList.vue -->
<template>
<div>
<ol>
<li v-for="fruit in fruits" :key="fruit">
{{ fruit }}
</li>
</ol>
<p>
Number of fruits: {{ numFruits }}
</p>
</div>
</template>
<script>
export default {
data() {
return {
fruits: ["apple", "banana", "orange"],
};
},
computed: {
numFruits() {
return this.fruits.length;
},
},
};
</script>
import { shallowMount } from '@vue/test-utils'
import FruitList from '../src/components/FruitList.vue'
describe('FruitList component test', () => {
test("tests data attributes", () => {
const wrapper = shallowMount(FruitList)
expect(wrapper.vm.fruits).toEqual(["apple", "banana", "orange"]);
})
})
The above test case tests data attributes, i.e. the initial value of the fruits array.
test('return total number of fruits', () => {
const wrapper = shallowMount(FruitList)
expect(wrapper.vm.numFruits).toBe(2); // it fails because fruits array has 3 elements not 2
expect(wrapper.vm.numFruits).toBe(3); // it passes
})
This tests the computed property numFruits has the correct value.
test('tests addFruit method', () => {
const wrapper = shallowMount(FruitList)
const vm = wrapper.vm
expect(vm.fruits.length).toBe(3)
// add mango to the fruit list
vm.addFruit('mango')
expect(vm.fruits.length).toBe(4)
})
test('displays a list of fruits', () => {
const wrapper = shallowMount(FruitList)
const fruits = wrapper.findAll('li')
expect(fruits.at(0).text()).toBe('apple')
expect(fruits.at(1).text()).toBe('banana')
expect(fruits.at(2).text()).toBe('orange')
})
test('displays the number of fruits', () => {
const wrapper = shallowMount(FruitList)
const numFruits = wrapper.find('p')
expect(numFruits.text()).toBe('Number of fruits: 3')
})
The above test cases verify the required data is being rendered properly.
When we test the component, it’s obvious that it consists of several assets like images and CSS files.
While running tests, Jest doesn’t handle non-JavaScript assets by default, and as such the test fails whenever it doesn’t find the respective assets.
We need to stub the assets used by a component. jest-transform-stub
is a Jest transformer that allows you to replace the imports of certain modules in your test files with a stub internally.
Simply adding the below configuration in jest.config.js, would save you from failing tests!
transform: {
".+\\.(css|scss|png|jpg|svg)$": "jest-transform-stub",
},
Here, all the css/scss/png/jpg/svg
will be stubbed while running the test, and jest
won’t make any complaints.
If you’re using @/
as the shorthand notation to the src directory, You will get an error for not finding a specific path like @/assets/image.png
.
Adding the below mapping in jest.config.js would disappear the error.
moduleNameMapper: {
"^@/(.*)$": "<rootDir>/src/$1",
},
If you have missed any of the steps above, find a full example at Vue3-component-test-example.
In this blog post, we have covered the following key points:
To sum up, writing tests can seem daunting at first, but by following the guidelines and best practices outlined in this blog post, you will be able to write effective tests for your Vue components with confidence.
Keep testing!!