VueJS (2.x) - Component Testing Helper - Part I

Pablo Veiga - May 26 '21 - - Dev Community

Software Testing can be defined as a method to check if the application that is being built matches expected requirements and is free of defects.
There are several types of software tests like shown in the image below:
Software Testing Types

Source: https://hackr.io/blog/types-of-software-testing

In the real world, it is quite hard to implement all of them within a project, mainly because of strict deadlines and lack of qualified people to do the job. But, there are at least two types of testing that can be easily implemented in a VueJS project by the developers themselves without much effort.

Unit Testing

Unit testing allows you to test individual units of code in isolation. It provides the development team with confidence to implement new features and refactor legacy code without compromising the application stability.

Component Testing

Component testing allows you to check if a component is fully working and displaying its state correctly. For that to be possible, components must be mounted to the DOM (either virtual or real).

In this article, I'm going to show how to build a useful and scalable helper that will assist you in implementing tests for your VueJS components very easily.


Creating a new VueJS project

Create a new VueJS project using Vue CLI, choose the Manually select features option and select the following options:

  • Babel
  • Linter / Formatter
  • Unit Testing

Vue Create - Manually select features

Choose 2.x and Jest in the Vue Version and Pick unit test solution steps respectively.

After the project has been created, install the following development dependencies in your project:

yarn add -D @testing-library/vue
Enter fullscreen mode Exit fullscreen mode

Building the component testing helper

Now, let's create a new file named test.js within /src/utils folder and import the functions that will help us instantiate a Vue class and render components:

import { createLocalVue } from '@vue/test-utils'
import { render } from '@testing-library/vue'

const localVue = createLocalVue()
Enter fullscreen mode Exit fullscreen mode

Then, create and export a customRender function that calls the Vue Testing Library render function and passes localVue to it along with the custom options required by each test (we will explore a few examples of custom options in part II of this article ;):

export const customRender = (component, options = {}) =>
  render(
    component,
      {
        localVue,
        ...options
      }
   )
Enter fullscreen mode Exit fullscreen mode

The final step is to export everything from @testing-library/vue. This isn't really necessary but it will help you keep just one line of import in your test files and, if you need, in the future, it might help you in case you need to change your test framework/library easily too.

export * from '@testing-library/vue'
Enter fullscreen mode Exit fullscreen mode

This will be enough for you to implement a simple test to a component.

Creating a simple component

Let's take a look at this simple component that is responsible for displaying a coloured label according to a given status:

<template>
  <span data-testid="status-label" :class="label.color">
    {{ label.text }}
  </span>
</template>
Enter fullscreen mode Exit fullscreen mode
import { STATUS } from "@/constants";

export default {
  name: "Status",
  props: ["status"],
  computed: {
    label() {
      const color = `Status__${this.status}`;

      if (this.status === STATUS.ERROR) {
        return {
          text: "Finished with errors",
          color,
        };
      } else if (this.status === STATUS.WARNING) {
        return {
          text: "Finished with warnings",
          color,
        };
      } else if (this.status === STATUS.SUCCESS) {
        return {
          text: "Finished",
          color,
        };
      }

      return {
        text: "Invalid Status",
        color: null,
      };
    },
  },
};
Enter fullscreen mode Exit fullscreen mode
<style lang="css" scoped>
.Status__error {
  color: red;
}
.Status__warning {
  color: orange;
}
.Status__success {
  color: green;
}
</style>
Enter fullscreen mode Exit fullscreen mode

Notice the data-testid attribute in the template. It is a good practice to determine unique data-testids per element within a component scope to identify its elements and make it easier to test.

Implementing tests

Now, using our test helper along with Jest, let's create a simple test suit to make sure the component is displaying the proper text and color according to the status prop value.

import "@testing-library/jest-dom/extend-expect";
import { customRender } from "@/utils/test";
import Status from "@/components/Status";

describe("Status", () => {
  it('should display a red label "Finished with errors" if status is equal to "error"', () => {
    const { getByTestId } = customRender(Status, {
      props: { status: "error" },
    });

    const statusLabel = getByTestId("status-label");
    expect(statusLabel).toHaveClass("Status__error");
    expect(statusLabel).toHaveTextContent("Finished with errors");
  });

  it('should display an orange label "Finished with warnings" if status is equal to "warning"', () => {
    const { getByTestId } = customRender(Status, {
      props: { status: "warning" },
    });

    const statusLabel = getByTestId("status-label");

    expect(statusLabel).toHaveClass("Status__warning");
    expect(statusLabel).toHaveTextContent("Finished with warnings");
  });

  it('should display a green label "Finished" if status is equal to "success"', () => {
    const { getByTestId } = customRender(Status, {
      props: { status: "success" },
    });

    const statusLabel = getByTestId("status-label");

    expect(statusLabel).toHaveClass("Status__success");
    expect(statusLabel).toHaveTextContent("Finished");
  });
});
Enter fullscreen mode Exit fullscreen mode

That was pretty easy right?!
You can find the full source-code of a working project that implements this helper in this link.

Soon, I will publish the second part of this article where we will improve this helper and make it customizable which allows you to test components that has external dependencies like Vuex or Vue I18n.

I hope you liked it!
Please, share and comment...

Cover image by Matthew Waring

. . . . . . . . . . . . . . . . . . . . . .