Getting Started with Vitest
Tags: coding, vue, javascript
Published: 2022-08-03 18:32:25

---
title: Getting Started with Vitest
published: true
date: 2022-08-01 10:00:03 UTC
tags: coding,vuejs,javascript
canonical_url: https://www.vuemastery.com/blog/getting-started-with-vitest
---

![Vitest — Getting Started](https://cdn-images-1.medium.com/max/1024/1*O11ieBU3eOW70brfPHGgRw.jpeg)

_Written by_ [_Timi Omoyeni_](https://timiomoyeni.com/)

### What is Vitest?

Since Evan You’s build tool Vite has gained massive popularity, there’s now a blazing fast unit test framework that is powered by it:  **Vitest**.

At a high level, Vitest is Jest-compatible and comes with out-of the-box ESM, Typescript, and JSX support, and it’s powered by esbuild. It uses the Vite dev server to transform your files during testing and listens to the same configuration of your app (through **vite.config.js** ), thereby eliminating the duplication involved in using testing alternatives such as Jest.

In this Vitest tutorial, we’ll look at why you’d want to use Vitest and how to get started using it to test your components.

### Why Vitest?

Vite is a build tool that aims to provide a faster and leaner development experience for modern web projects with out-of-the-box support for common web patterns, features like glob imports and SSR primitives. Its many plugins and integrations are fostering a vibrant ecosystem.

But this has led to a new problem: writing unit tests for applications that run on Vite.

Using frameworks like Jest with Vite has led to a lot of duplications in configuration between Vite and Jest, which Vitest solves for by removing the extra configuration chore needed to write unit tests for our applications. Vitest uses the same configuration as Vite, and shares a common transformation pipeline during development, build and test time. It is also extensible using the same plugin API as Vite and offers compatibility with [Jest’s API](https://vitest.dev/api/) for easy migration away from Jest without needing to do much refactoring.

As a result of all this, Vitest is _very_ fast. So let’s get started using it.

### How to use Vitest to test your components

### How to install Vitest

Adding Vitest to your project requires that you have at least Vite >=v2.7.10 and Node >=v14 to work.

You can install Vitest using npm, yarn, or pnpm using the following commands in your terminal depending on your preference.

### [NPM](https://www.npmjs.com/)

```bash
npm install -D vitest
```

### [YARN](https://yarnpkg.com/)

```bash
yarn add -D vitest
```

### [PNPM](https://pnpm.io/)

```bash
pnpm add -D vitest
```

![](https://cdn-images-1.medium.com/max/703/0*5SroXBtJ_jigeZV2)

### Vitest Configuration

After installing Vitest, the next thing we need to do is add it to our **vite.config.js** file:

**vite.config.js**

```jsx
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";

export default defineConfig({
    plugins: [vue()],
    _//add test to vite config_
    test: {
        _// ..._
    },
});
```

Configuring Vitest for TypeScript is similar, but we need to add a reference to Vitest types using the [triple slash command](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html#-reference-types-) at the top of our config file if importing defineConfig from Vite.

```tsx
_/// <reference types="vitest" />_
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
_// https://vitejs.dev/config/_

export default defineConfig({
    plugins: [vue()],
    test: {
        _// ..._
    },
});
```

It’s important to note that Vitest can also be configured in your project by adding a **vitest.config.js** file in the root folder (same location as **vite.config.js** ). If this file is present, it takes precedence over the **vite.config.js** for configuring Vitest. Vitest also allows for extra configuration which can be found in the [config page](https://vitest.dev/config/#configuration).

### Our Demo Example: A Notification

To see Vitest in action, we’re going to create a notifications component that displays three types of notifications: _info, error,_ and _success_. Each state of this component looks like this.

### info

![](https://cdn-images-1.medium.com/max/600/0*vnsZTUQTN81AV0fT)

### error

![](https://cdn-images-1.medium.com/max/600/0*VvKC51yFOJkUgytN)

### success

![](https://cdn-images-1.medium.com/max/600/0*KxGy5w7eEAC9sxGT)

Get familiarized with this component, because this is what we’ll be writing our tests for.

**notification.vue**

```html
<template>
  <div
    :class="[
      'notification',
      type === 'error' ? 'notification--error' : null,
      type === 'success' ? 'notification--success' : null,
      type === 'info' ? 'notification--info' : null,
      message && message.length > 0 ? 'notification--slide' : null,
    ]"
  >
    <img
      src="https://res.cloudinary.com/djalafcj9/image/upload/v1634261166/getequityV2/denied_sbmv0e.png"
      v-if="type === 'error'"
    />
    <img
      src="https://res.cloudinary.com/djalafcj9/image/upload/v1656690265/getequityV2/Frame_irxz3e.png"
      v-if="type === 'success'"
    />
    <img
      src="https://res.cloudinary.com/djalafcj9/image/upload/v1634261166/getequityV2/pending_ctj1ke.png"
      v-if="type === 'info'"
    />
    <p class="notification__text">
      {{ message }}
    </p>
    <button
      ref="closeButton"
      class="notification__button"
      @click="$emit('clear-notification')"
    >
      <img
        src="https://res.cloudinary.com/djalafcj9/image/upload/v1635485821/getequityV2/close_muxdyb.png"
      />
    </button>
  </div>
</template>
<script>
  export default {
    name: "Notification",
    emits: ['clear-notification'],
    props: {
      type: {
        type: String,
        default: null,
      },
      message: {
        type: String,
        default: null,
      },
    },
  };
</script>

<style>
  .notification {
    transition: all 900ms ease-out;
    opacity: 0;
    z-index: 300001;
    transform: translateY(-100vh);
    box-sizing: border-box;
    padding: 10px 15px;
    width: 100%;
    max-width: 730px;
    _/\* margin: 0 auto; \*/_
    display: flex;
    position: fixed;
    _/\* left: 0; \*/_
    top: 20px;
    right: 15px;
    justify-content: flex-start;
    align-items: center;
    border-radius: 8px;
    min-height: 48px;
    box-sizing: border-box;
    color: #fff;
  }

  .notification--slide {
    transform: translateY(0px);
    opacity: 1;
  }

  .notification--error {
    background-color: #fdecec;
  }

  .notification__text {
    margin: 0;
    margin-left: 17px;
    margin-right: auto;
  }

  .notification--error .notification__text {
    color: #f03d3e;
  }

  .notification--success {
    background-color: #e1f9f2;
  }

  .notification--success > .notification__text {
    color: #146354;
  }

  .notification--info {
    background-color: #ffb647;
  }

  .notification__button {
    border: 0;
    background-color: transparent;
  }
</style>
```

Here, we create a component that displays a dynamic message using the message prop. We also make use of the type prop to style the background and text of this component, and display the different icons (error, success, and info) we plan to have using this type prop.

Finally, we have a button that is used to dismiss the notification by emitting a custom event: clear-notification.

### What should we be testing?

Now that we have an understanding of the structure of the component that needs to be tested, we can ask ourselves what this component needs to do, to be functioning as intended.

Our tests need to check for the following:

1. The component renders the correct style depending on the notification type.
2. The notification fades away when message is empty.
3. The component emits an event when the close button is clicked.

With these goals in mind, we can start fleshing out the test file for this component. Let’s add a new **tests** folder inside our project and create a file called **notification.test.js** inside it.

### Installing our testing dependencies

When writing unit tests, there may be instances where we need to replace the existing implementation of the component with a dummy component that doesn’t really do anything. This is called a **stub** , and to use a stub in our tests, we’ll need access to the mount method from [Vue Test Utils](https://test-utils.vuejs.org/), the official testing utility library for Vue.js.

Let’s install Vue Test Utils now.

### Installation

```bash
npm install --save-dev @vue/test-utils@next

# or

yarn add --dev @vue/test-utils@next
```

Now, inside our test file, we can import mount from "@vue/test-utils"

**notification.test.js**

```jsx
import { mount } from "@vue/test-utils";
```

In our tests, we will also need to be able to mock the DOM. Vitest currently supports both [happy-dom](https://github.com/capricorn86/happy-dom) and [jsdom](https://github.com/jsdom/jsdom). For this demo, we’re going with happy-dom, which we’ll also install now.

### Installation

```bash
yarn add happy-dom --dev
```

After installation, we can either add the following comment to the top of our test file…

**notification.test.js**

```jsx
_/\*\*  
 \* @vitest-environment happy-dom  
 \*/_  
```

… or add this to the vite/vitest config file to avoid repetition when there is more than one test file that needs happy-dom to work.

**vite.config.js**

```jsx
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
_// https://vitejs.dev/config/_
export default defineConfig({
    plugins: [vue()],
    test: {
        environment: "happy-dom",
    },
});
```

Since we only have one test file, we can afford to go with the first option. At this point, our test file should look like this;

**notification.test.js**

```jsx
_/\*\*  
 \* @vitest-environment happy-dom  
 \*/_  

import { mount } from "@vue/test-utils";
```

With these dependencies in place, we can now import the component that we’re testing.

**notification.test.js**

```jsx
_/\*\*  
 \* @vitest-environment happy-dom  
 \*/_  

import { mount } from "@vue/test-utils";
import notification from "../components/notification.vue";
```

We’re now ready to start writing the tests for the different cases we identified earlier.

### Common Vitest Methods

To write our tests, we need to make use of the following common methods, which can be imported from Vitest:

- describe: This function accepts a name and a function and is used to group related tests together. It comes in handy when you’re writing tests for a component that has multiple test points (e.g logic and appearance).
- test/it: This function represents the actual block of code that gets tested. It accepts a string which is usually the name or description of the test case (e.g renders the correct style for success) and another function where all the checks and testing take place.
- expect: This function is used to test values or create assertions. It accepts an argument **_x_** that is expected to be an actual value (string, number, object, etc) and evaluates it using any of the supported methods (e.g toEqual(y) which checks if **_x_** is the same as y).

So let’s import these into our test file now

**notification.test.js**

```jsx
_/\*\*  
 \* @vitest-environment happy-dom  
 \*/_  

import { mount } from "@vue/test-utils";
import notification from "../components/notification.vue";
import { describe, expect, test } from "vitest";
```

With these functions accessible, let’s start constructing our unit tests.

### Building our Vitest Unit Tests

We will first use the describe method to group our tests together.

**notification.test.js**

```jsx
describe("notification.vue", () => {
    
});
```

Inside the describe block, we will add each of our actual tests.

As a reminder, our first test case is: _The component renders the correct style depending on the notification type._

**notification.test.js**

```jsx
describe("notification.vue", () => {
    test("renders the correct style for error", () => {

    });
});
```

Here, renders the correct style for error represents the **_name_** of what the test is checking for. It helps provide context into what the block of code is checking for so that it can be easily maintained and updated by someone other than the original author. It also makes it easy to identify a particular failing test case;

![](https://cdn-images-1.medium.com/max/596/0*mjQ8YL3uHVSbOpBm)

**notification.test.js**

```js
describe("notification.vue", () => {
    test("renders the correct style for error", () => {
        const type = "error";
       
    });
});
```

If we go back to [our component](https://www.vuemastery.com/blog/getting-started-with-vitest#message), we define a type prop that accepts a string that is used to determine things like background color, type of icon, and text color to render on the component. Here, we create a variable type and assign to it one of the types that we’re working with, **error** (from error, info, or success).

**notification.test.js**

```jsx
describe("notification.vue", () => {
    test("renders the correct style for error", () => {
        const type = "error";
        const wrapper = mount(notification, {
            props: { type },
        });
    });
});
```

Here, we use mount to stub our component for the purpose of testing. mount accepts the component as the first argument and a list of options as the second argument. These options provide different properties that aim to ensure your components work as they would in your browser and from this list, we only need the props property. We make use of this property because our notification.vue component needs at least one **_prop_** for it to work effectively. After adding this option, we pass in the list of our props (in this case, we only have a type prop).

**notification.test.js**

```jsx
describe("notification.vue", () => {
    test("renders the correct style for error", () => {
        const type = "error";
        const wrapper = mount(notification, {
            props: { type },
        });
        expect(wrapper.classes()).toEqual(
            expect.arrayContaining(["notification--error"])
        );
    });
});
```

At this point, all that is left is to write an assertion or better yet, the expected behavior of our component which is; renders the correct style for error, and to do this, we have made use of the [expect](https://www.vuemastery.com/blog/getting-started-with-vitest#expect) method. It accepts our stubbed component with all the options (in our case, we’ve named it wrapper for easy reference). This method can be chained to a number of other methods but for this particular assertion, we’re checking that the component’s class list returns an array that includes this notification--error. We achieve this using the [classes](https://test-utils.vuejs.org/api/#classes) function which returns the array containing all the classes for the component. After this, the next thing would be to compare it using the [toEqual](https://vitest.dev/api/#toequal) function, which checks that a value **X** is equal to **Y.** Inside this function, we check that it returns an array that contains our class, notification--error.

If we repeat the same process for the other notification type; success and info, we would have the following;

```jsx
import { mount } from "@vue/test-utils";
import notification from "../components/notification.vue";
import { describe, expect, test } from "vitest";
describe("notification.vue", () => {
    test("renders correct style for error", () => {
        const type = "error";
        const wrapper = mount(notification, {
            props: { type },
        });
        expect(wrapper.classes()).toEqual(
            expect.arrayContaining(["notification--error"])
        );
    });

    test("renders correct style for success", () => {
        const type = "success";
        const wrapper = mount(notification, {
            props: { type },
        });
        expect(wrapper.classes()).toEqual(
            expect.arrayContaining(["notification--success"])
        );
    });

    test("renders correct style for info", () => {
        const type = "info";
        const wrapper = mount(notification, {
            props: { type },
        });
        expect(wrapper.classes()).toEqual(
            expect.arrayContaining(["notification--info"])
        );
    });

    test("slides down when message is not empty", () => {
        const message = "success";
        const wrapper = mount(notification, {
            props: { message },
        });
        expect(wrapper.classes()).toEqual(
            expect.arrayContaining(["notification--slide"])
        );
    });
});
```

At this point, we’ve written tests to ensure our notifications are being styled according to their type. When users click on the close button on the component, we reset the [message](https://www.vuemastery.com/blog/getting-started-with-vitest#message) prop. According to our code, we’re adding or removing a notification--slide class depending on the value of this message prop as seen below.

**notification.vue**

```html
<div
    :class="[
      'notification',
      type === 'error' ? 'notification--error' : null,
      type === 'success' ? 'notification--success' : null,
      type === 'info' ? 'notification--info' : null,
      message && message.length > 0 ? 'notification--slide' : null,
    ]"
  >
//...
```

If we’re to test for this particular assertion, it would look like this:

```jsx
test("slides up when message is empty", () => {
        const message = "";
        const wrapper = mount(notification, {
            props: { message },
        });
        expect(wrapper.classes("notification--slide")).toBe(false);
    });
```

In this block of test code, we create a message variable with an empty string and pass it as a prop to our component. After this, we check the class array of our component and ensure it does not include the notification--slide class, which is responsible for making our component slide down/out into the user’s view. To do this, we use the [toBe](https://vitest.dev/api/#tobe) function, which takes a value **A** , and tries to check it **_is the same_** as  **B.**

We also want to test that it emits an event whenever the button on the component is clicked.

```jsx
test("emits event when close button is clicked", async() => {
        const wrapper = mount(notification, {
            data() {
                return {
                    clicked: false,
                };
            },
        });
        const closeButton = wrapper.find("button");
        await closeButton.trigger("click");
        expect(wrapper.emitted()).toHaveProperty("clear-notification");
    });
```

In this test block, we make use of an **_async_** function because we are going to be triggering an event, which returns a promise and we need to wait for this promise to resolve in order to catch the changes this event would cause. We also make use of the data function and add a clicked property which would be toggled when clicked.

At this point, we need to trigger this click event and we do so by first using the [find](https://test-utils.vuejs.org/api/#find) function to get the button. This function is the same as querySelector as it accepts either a class, an id, or an attribute and returns an element.

After finding our button, we use the [trigger](https://test-utils.vuejs.org/api/#trigger) method to trigger a click event. This method accepts the event name (_click, focus, blur, keydown, etc.)_ that is to be triggered and performs this event and returns a promise. For this reason, we _await_ this action in order to ensure the changes to our DOM have been made before we make an assertion based on this event.

Finally, we check the list of events that our component has emitted using the [emitted](https://test-utils.vuejs.org/api/#emitted) method which returns an array. We then check that this array includes the clear-notification event.

Finally, we test to ensure that our component renders the right message being passed to the message prop. This is what we would have:

```jsx
test("renders message when message is not empty", () => {
        const message = "Something happened, try again";
        const wrapper = mount(notification, {
            props: { message },
        });
        expect(wrapper.find("p").text()).toBe(message);
    });
```

Here, we create a message variable, assign a random string to it and pass it as a prop to our component. We then search for our message text using the p tag since this is where the message is being displayed and check that its text is the same as the message prop. p here can also be a class name or an attribute but we’re going with the tag name since there is only one paragraph tag in the component. We extract the content of this tag using [text](https://test-utils.vuejs.org/api/#text) method, which is quite similar to [innerText](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText). Finally, we assert that this value would be the same as message using the same function from earlier, toBe.

### Our complete test file

After covering all these, here is what the complete test file looks like.

**notification.test.js**

```jsx
_/\*\*  
 \* @vitest-environment happy-dom  
 \*/_  

import { mount } from "@vue/test-utils";
import notification from "../components/notification.vue";
import { describe, expect, test } from "vitest";

describe("notification.vue", () => {
    test("renders the correct style for error", () => {
        const type = "error";
        const wrapper = mount(notification, {
            props: { type },
        });
        expect(wrapper.classes()).toEqual(
            expect.arrayContaining(["notification--error"])
        );
    });

    test("renders the correct style for success", () => {
        const type = "success";
        const wrapper = mount(notification, {
            props: { type },
        });
        expect(wrapper.classes()).toEqual(
            expect.arrayContaining(["notification--success"])
        );
    });

    test("renders the correct style for info", () => {
        const type = "info";
        const wrapper = mount(notification, {
            props: { type },
        });
        expect(wrapper.classes()).toEqual(
            expect.arrayContaining(["notification--info"])
        );
    });

    test("slides down when message is not empty", () => {
        const message = "success";
        const wrapper = mount(notification, {
            props: { message },
        });
        expect(wrapper.classes()).toEqual(
            expect.arrayContaining(["notification--slide"])
        );
    });

    test("slides up when message is empty", () => {
        const message = "";
        const wrapper = mount(notification, {
            props: { message },
        });
        expect(wrapper.classes("notification--slide")).toBe(false);
    });

    test("emits event when close button is clicked", async() => {
        const wrapper = mount(notification, {
            data() {
                return {
                    clicked: false,
                };
            },
        });
        const closeButton = wrapper.find("button");
        await closeButton.trigger("click");
        expect(wrapper.emitted()).toHaveProperty("clear-notificatioon");
    });

    test("renders message when message is not empty", () => {
        const message = "Something happened, try again";
        const wrapper = mount(notification, {
            props: { message },
        });
        expect(wrapper.find("p").text()).toBe(message);
    });
});
```

A few things to note:

1. We make use of [mount](https://test-utils.vuejs.org/api/#mount) for stubbing the component we’re trying to test, which is made available by [Vue Test Utils](https://yarnpkg.com/package/@vue/test-utils). (yarn add --dev @vue/test-utils@next)

### Running the tests

Now that we’re done writing our tests, we need to run them. To do this, we go to **package.json** and add the following lines to our script section:

**package.json**

```json
"scripts": {
        "test": "vitest",
        "coverage": "vitest run --coverage"
    },
```

If we run yarn vitest or yarn test in our terminal, our test file gets run and we should see the test results and a breakdown.

![](https://cdn-images-1.medium.com/max/531/0*zb49C674XnDWlz4Q)

At this point, we have successfully run our first tests using Vitest. One thing to note from the result is that this command would only need to be run once and gets re-run as we make updates and changes to our test files, due to Vitest’s smart and instant watch mode.

### Wrapping up

Using Vitest for unit testing your application is seamless and requires fewer steps to get up and running than alternatives like Jest. Vitest also makes it easy to migrate your existing tests from Jest to Vitest without a need for extra configuration.

If you’re interested in more testing content, check out Vue Mastery’s [Unit Testing Vue 3](https://www.vuemastery.com/courses/unit-testing-vue-3/what-to-test-vue-3) and [Real World Testing](https://www.vuemastery.com/courses/real-world-testing/getting-started) courses.

_Originally published at_ [_https://www.vuemastery.com_](https://www.vuemastery.com/blog/getting-started-with-vitest) _on August 1, 2022._

* * *