Vue Test Utils

If you are using Vue for your development project, there are robust functions available to help you validate the code and test app components. Alternatively, you can hire a reputable VueJS development company to handle your testing requirements. 

But since it’s your project, you must understand your options and how to use them for Vue testing. In this article, we will explore the Vue Test Utils library, which provides a set of helper functions to assist developers in testing application components. 

Let’s take a look at it in detail and learn how we can use it to test Vue apps.

1. What are Vue Test Utils?

Vue Test Utils is an official library that provides a set of utility functions for testing VueJS components. It offers methods for mounting and interacting with Vue components in isolation. The developers refer to this library as a wrapper. 

The abstraction of the mounted component, referred to as the wrapper, provides utility functions that simplify various testing tasks. Developers use these methods to execute specific inputs like store changes, or user actions, which help verify whether the output, such as function calls or Vue events, is correct. 

The set of helper functions from Vue Test Utils allows you to test stores, execute asynchronous behaviors, make HTTP requests, and validate different attributes of a component. It also provides individual stubs or the shallowMount for mocking and rendering the stub components. 

2. Setting Up Our Testing Environment

We can begin by creating a new folder first and then starting a new Vue.JS project by running the following command. 

$ npm init vue@latest

Running the above command will install and execute the official Vue project scaffolding tool known as create-vue.  Afterward, the screen will prompt you to select suitable installation features from the given options, as shown below. 

Click Yes for JSX support, ESLint, and Prettier, and No for the rest of the options.

vue@latest

Next, run the given code to install the required dependencies and start a development server.

$ cd vue-test-utils
$ npm install
$ npm run dev
cd vue-test-utils
Created successfully project with Vite + Vue 3

To add the Vue Test Utils, execute the command given below:

$ npm install --save-dev @vue/test-utils
# or
$ yarn add --dev @vue/test-utils
--save-dev @vue/test-utils

Vue Test Utils is flexible enough to work with various test runners allowing you to choose the one that best suits your needs. In this tutorial, we are going to use Jest. To install vue-jest and load .vue files in Jest, follow these steps: 

$ npm install --save-dev vue-jest

To install jest, run:

$ npm install --save-dev jest
npm install --save-dev jest

Open your jest.config.js project folder to create a new file and add the below code to it.

module.exports = {
  preset: "ts-jest",
  globals: {},
  testEnvironment: "jsdom",
  transform: {
    "^.+\\.vue$": "vue-jest",
    "^.+\\js$": "babel-jest",
  },
  moduleFileExtensions: ["vue", "js", "json", "jsx", "ts", "tsx", "node"],
};

3. Create Your First Test

Let’s go through the step-by-step process of creating a test for a basic demo application using Vue Test Utils. First of all, open the App.vue and create a demo TCPL component. 

<template>
  <div></div>
</template>
<script lang="ts">
export default {
  name: 'TCPLApp',
  data() {
    return {
      team: [
        {
          id: 1,
          name: 'Darshan Patel',
          isSelected: false,
        },
      ],
    }
  },
}
</script>

Open the App.spec.ts and add the following test:

import { mount } from '@vue/test-utils'
import App from './App.vue'
test('renders a player', () => {
  const wrapper = mount(App)
  const player = wrapper.get('[data-test="player"]')
  expect(player.name()).toBe('Darshan Patel')
})

In this test, we are rendering the component using the Vue Test Utils’ mount. To simulate the rendering, you invoke the mount and pass the component as the first argument. Now, locate an element with the selector data-test=”player” corresponding with the DOM element like <div data-test=”player”>…</div>. the next step would be to retrieve the content using the text methods and verify if it matches “Darshan Patel.”

The team array is rendered by updating the <template> in the App.vue.

<template>
  <div>
    TCPL Players
    <div v-for="player in team" :key="player.id" data-test="player">
      {{ player.name }}
    </div>
  </div>
</template>

If the test passes:

PASS  src/App.spec.ts

  ✓ renders a player(48ms)
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.257s
Ran all test suites.
  Done in 6.28s.

That was your first successful component test.

We can add functionality that allows users to add new players. For that, we need to create a form with an input field where the users can enter text. A new player will appear on the screen once the user submits the form. The test given below shows you how to do it. Leveraging this functionality, users can create a whole team if they want.

import { mount } from "@vue/test-utils";
import App from "./App.vue";
test("creates a team", async () => {
  const wrapper = mount(App);
  await wrapper.get('[data-test="new-team"]').setValue("New team");
  await wrapper.get('[data-test="form"]').trigger("submit");
  expect(wrapper.findAll('[data-test="player"]')).toHaveLength(2);
});

The mount method is used to render the component in this test. According to our assertion, only one player should be displayed. Therefore, the last line of code mentions that we are adding a new player. Using setValue methods, we can update the input value, which helps us modify the input field.

Once the input is updated, use the trigger method to simulate the action of the user submitting the form. As a result, you can see that the player moved up from position 1 to position 2. However, this test will fail when you execute it. So, add the form and input elements to update the App.vue file.

<template>
  <div>
    <div v-for="player in team" :key="player.id" data-test="player">
      {{ player.name }}
    </div>
    <form data-test="form" @submit.prevent="createTeam">
      <input data-test="new-player" v-model="newPlayer" />
    </form>
  </div>
</template>
<script lang="ts">
export default {
  name: "TCPLApp",
  data() {
    return {
      newPlayer: "",
      team: [
        {
          id: 1,
          name: "Darshan Patel",
          isSelected: true,
        },
      ],
    };
  },
  methods: {
    createTeam() {
      this.team.push({
        id: 2,
        text: this.newPlayer,
        isSelected: true,
      });
    },
  },
};
</script>

In the code above, we use v-model to bind the input and @submit to listen for the form submission event. The createTeam function is called upon form submission and adds a new player to the specified team array. 

PASS  src/App.spec.ts
  ✓ adds a player to the team (21ms)
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.294s, estimated 2s
Ran all test suites.
Done in 4.83s.

4. Conditional Rendering

To ensure the functionality of the Vue components, Vue Test Utils (VTU) provides various features that help render the components and verify their state. Here, we are going to learn how to render Vue components and check whether their content is displayed correctly. The v-if, a critical feature of Vue, allows you to add or remove elements dynamically. In this example, we are testing a component that uses v-if.

const File = {
  template: `
  <nav>
     <a id="tcpl-dashboard" href="/player-profiledashboard">PLayer ProfileTCPL Dashboard</a>
     <a v-if="completed" id="completed" href="/tcpl-dashboard">TCPL Dashboard</a>
  </nav>
    `,
  data() {
    return {
      selected: false,
    };
  },
};

The given <File> component displays a link to the user’s profile. If the selected value is true, we will be able to see a link to the TCPL dashboard. In this test, we are verifying whether three different scenarios are working correctly. 

  1. The /player-profile link should be visible.
  2. The /tcpl-dashboard link must appear when the user selects a player. 
  3. The /tcpl-dashboard link must not appear when the user doesn’t select any player. 

4.1 get() Method

The get() method from the Vue Test Utils (VTU) wrapper uses the querySelector syntax to locate an element. We will use this get() method to assign the content of the TCPL dashboard link. 

test("renders the TCPL dashboard link", () => {
  const wrapper = mount(File);
  const tcplDashboardLink = wrapper.get("#tcpl-dashboard");
  expect(tcplDashboardLink.text()).toEqual("TCPL Dashboard");
});

The get() method throws an error if it can’t find an element matching the selector, causing the test to fail. If it finds the element, get() returns a DOMWrapper. It is recommended not to use the get() method to find elements because an error is thrown if the method assumes that elements do not exist. 

Instead, we can use the find and exist methods from the Vue Test Utils in such scenarios. The code below shows how to check if subscribed is false and whether the user dashboard link is present. 

4.2 Find and Exists Methods

test("does not render TCPL dashboard link", () => {
  const wrapper = mount(Nav);
  // If we directly use `wrapper.get` the test would fail.
  expect(wrapper.find("#completed").exists()).toBe(false);
});

5. Testing Emitted Events

Interactions between Vue components occur through props and by using the $emit method to trigger events. To verify that events are emitted correctly, we can use VTU’s emitted() function. 

In this example, we have a simple Counter component with a button. It increases the internal count variable and emits the updated value with a single click of the button. 

const clickCounter = {
  template: '<button @click="onClick">Increase</button>',
  data() {
    return {
      clickCount: 0,
    };
  },
  methods: {
    onClick() {
      this.clickCount += 1;
      this.$emit("increase", this.clickCount);
    },
  },
};

To test this component, we need to verify that it emits an “increase” event with the latest clickcount value. When we use the emitted() function, it returns an object containing all the events emitted by the component, along with their arguments in an array. 

5.1 emitted() Method

test("triggers an event upon being clicked", () => {
  const clickWrapper = mount(clickCounter);
  clickWrapper.find("button").trigger("click");
  clickWrapper.find("button").trigger("click");
  expect(clickWrapper.emitted()).toHaveProperty("increase");
});

As you can see in the test above, the emitted() method returns an object containing keys to corresponding events emitted by the component. In this scenario, the key is to increase. Once we confirm that the emitted event has the correct name, the test is considered successful. Here, we used the trigger() method to simulate the user interactions. Visit this page to learn more about how to assert complex events. 

6. Testing Forms

In this section, we will learn how to interact with the form elements, set values, and trigger events. Let’s take a basic form, for example:

<template>
  <div>
    <input type="email" v-model="playerEmail" />
    <button @click="onSubmit">Submit</button>
  </div>
</template>
<script>
export default {
  data() {
    return {
      playerEmail: "",
    };
  },
  methods: {
    onSubmit() {
      this.$emit("onSubmit", this.playerEmail);
    },
  },
};
</script>

The v-model directive is widely used to bind input data in Vue. The setValue method is used for modifying the input value in the Vue Test Utils. Taking a parameter such as a boolean or a string, this method returns a promise that is resolved once the DOM is updated by Vue. 

6.1 setValue() Method

test("assigns the value", async () => {
  const componentWrapper = mount(Component);
  const input = componentWrapper.find("input");
  await input.setValue("[email protected]");
  expect(input.element.value).toBe("[email protected]");
});

You can see that in the test above, we have updated the input element’s value property to our given value using the setValue method. We also used the await to ensure that the Vue updated the value and the change was reflected in the DOM before we made any assertions. 

Triggering events is a crucial process to understand when working with forms and action elements. We will take the button example from before. 

<button @click="onSubmit">Submit</button>

In the above command, we used the trigger()method to simulate a click event. 

6.2 Test trigger() Method

test("trigger", async () => {
  const triggerWrapper = mount(Component);
  await triggerWrapper.find("button").trigger("click");
  // Verify that an action, such as triggering an event, has occurred
  expect(triggerWrapper.emitted()).toHaveProperty("submit");
});

In this test, we invoked the Vue component’s onSubmit method by triggering the click event listener. As we did in the setValue method, we will use the await to ensure that the Vue reflects the action. Next, we will assert whether the emitted event is correct to verify the expected outcome. 

Now, we are going to combine these two methods to verify that our form is emitting the correct user inputs.

test("sends the input value to its parent component.", async () => {
  const emitWrapper = mount(Component);
  await emitWrapper.find("input").setValue("[email protected]");
  await emitWrapper.find("button").trigger("click");
  // Verify that the submit event is triggered,
  expect(emitWrapper.emitted("submit")[0][0]).toBe("[email protected]");
});

VTU is very helpful for creating tests for Vue components, but there are a few limitations as well. But if you write a test before creating the component, it provides you clarity about the quality of code you need to write. 

Focus on writing code that is both testable and maintainable. Also, create a test that is meaningful and easy to run.  Make sure you develop a small and focused component. They are easy to understand and compose. The fewer tasks a component handles, the easier it is to test them. 

More importantly, always consider the inputs, such as data streams and props, and outputs, like events and DOM elements, from the user’s perspective. 

7. Conclusion

In this tutorial, we explored Vue Test Utils for Vue 3 and provided a step-by-step guide to set up the testing environment, how to write the tests, and how to test the Vue app components. We also discussed how to handle conditional rendering, test emitted events, and manage form elements, along with real-world examples. There is no doubt that VTU simplifies testing for Vue developers. 

FAQs 

What is the difference between Vue Test Utils and Jest?

Vue Test Utils is an official library providing utility functions for testing VueJS app components. It enables you to perform both unit testing and integration testing. Meanwhile, Jest is a JavaScript framework that ensures the correctness of any JavaScript codebase. 

What is the test function of the Vue test utils?

Vue Test Utils provides an isolated environment for interacting with Vue components. It also offers a set of utility and helper functions for testing stores, executing asynchronous behaviors, making HTTP requests, and validating the attributes of Vue components. 

profile-image
Parind Shah

Parind Shah is responsible for frontend innovations at TatvaSoft. He brings profound domain experience and a strategic mindset to deliver exceptional user experience. He is always looking to gain and expand his skill set.

Comments

  • Leave a message...