Dev Ramble

Guides, how-to's and other nonsensical ramblings from a software engineer

Home

Using Manual Mocks for Imported Node Modules with Vue + Jest

May 10, 2020

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>
AxiosExample.vue

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.

Manual Mock Location

Location of manual mock

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;
axios.js

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');
  });
});
AxiosExample.spec.js

Share This Post