Jest is a great test framework to use for unit testing Vue.js components. However mocking Node modules can be a bit tricky. In this blog post I’ll explain a method I’ve used in the past to mock an imported Node module like axios.
What We Are Testing
In this example, we’ll be testing a simple Vue component that makes a single API request using axios
and saves the response in the component’s data property.
The component has one button, which triggers the callApi
method when clicked. This method will use axios
to create the HTTP request and saves the relevant data from the response to the imageUrl
property. The imageUrl
property is bound to the <img>
to display a random image of a 🐶.
We’ll be testing the functionality in the callApi
method by mocking axios
to return some placeholder data.
<template>
<div>
<button @click="callApi">🐶</button>
<img :src="imageUrl" />
</div>
</template>
<script>
import axios from "axios";
export default {
name: "AxiosExample",
data: function() {
return {
imageUrl: ""
};
},
methods: {
callApi: function() {
return axios({
methods: "get",
url: "https://random.dog/woof.json"
}).then(resp => {
this.imageUrl = resp.data.url;
});
}
}
};
</script>
Creating the Manual Mock
Manual Mocks allow you to completely override an imported module by writing your own mock version of the module. This is done by creating a file with the same name as the imported file and placing it inside a __mocks__
folder.
The __mocks__
folder should be placed in the same location as the file you are mocking. For the case where you are mocking a node module, the folder should be placed adjacent to the node_modules
folder which will typically be at the root of your project.
Since we’ll be mocking axios
, we’ll create an axios.js
file inside a __mocks__
folder at the root of our project directory. Inside the axios.js
file, we’ll create our own mock implementation of axios
and export it. Our mock implementation will simply consist of a mock function that returns a resolved promise with some mock response data.
// The mock response data that will be returned from axios
const response = {
data: {
url: 'abc.xyz'
}
};
// A mock function that returns a resolved promise with the above response
const axios = jest.fn(() => Promise.resolve(response));
export default axios;
Writing the Test
The test is a typical Vue + Jest unit test. Check out the Vue.js documentation for more details on unit testing Vue components.
In our test, we’ll be importing axios
. However, we won’t actually be using the real axios
. Jest will be using the axios
created in the __mocks__
folder if one exists.
Now all we need to do is mount the component and call the method under test (callApi
). Note that we need to await
the method call as callApi
is returning a promise. Since the end goal of the test if to check whether imageUrl
has been correctly set, we need to wait until the promise and chained .then()
has resolved.
Also, since we implemented axios
as a mock function using jest.fn()
, we can also check whether it has been called using expect().toHaveBeenCalled()
.
import { shallowMount } from "@vue/test-utils";
import AxiosExample from "@/components/AxiosExample.vue";
import axios from 'axios';
describe("AxiosExample.vue", () => {
it("sets imageUrl after API is called", async () => {
// Mount the AxiosExample component
const wrapper = shallowMount(AxiosExample);
// Call the method we are testing
await wrapper.vm.callApi();
// Check that our mock axios() function has been called
expect(axios).toHaveBeenCalled();
// Check that imageUrl has been set to our mocked response data
expect(wrapper.vm.imageUrl).toEqual('abc.xyz');
});
});