VueJS (2.x) - Component Testing Helper - Part I
Tags: vue, javascript, test, beginners
Published: 2021-05-26 22:52:12

_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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x9g0hwkfc2pxd1xw7c6c.png)

_Source: [https://hackr.io/blog/types-of-software-testing](hackr.io)_


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)
- [Component Testing](#component-testing)
 

<span id="unit-testing"></span>
### 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.


<span id="component-testing"></span>
### 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](https://vuejs.org/v2/guide/render-function.html#The-Virtual-DOM) 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](https://cli.vuejs.org/), choose the `Manually select features` option and select the following options:

- Babel
- Linter / Formatter
- Unit Testing

![Vue Create - Manually select features](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/slb09pmtsxi825cbq3uu.png)
 
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:

- [Vue Testing Library] (https://testing-library.com/)

```shell
yarn add -D @testing-library/vue
```

## 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:

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

const localVue = createLocalVue()
```

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 ;):

```javascript
export const customRender = (component, options = {}) =>
  render(
    component,
      {
        localVue,
        ...options
      }
   )
```

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.


```javascript
export * from '@testing-library/vue'
```

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:

```html
<template>
  <span data-testid="status-label" :class="label.color">
    {{ label.text }}
  </span>
</template>
```

```javascript
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,
      };
    },
  },
};
```

```css
<style lang="css" scoped>
.Status__error {
  color: red;
}
.Status__warning {
  color: orange;
}
.Status__success {
  color: green;
}
</style>
```

_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](https://jestjs.io/), 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.


```javascript
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");
  });
});
```
---

That was pretty easy right?! 
You can find the full source-code of a working project that implements this helper in [this link](https://github.com/vcpablo/vuejs-test-helper).

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](https://vuex.vuejs.org/) or [Vue I18n](https://kazupon.github.io/vue-i18n/).

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

_Cover image by [Matthew Waring](https://unsplash.com/@matthewwaring)_