Building Vue3 Component Library from Scratch #12 Vitest
Tags: vitest, vue
Published: 2024-05-30 03:54:34

Vitest is a high-performance front-end unit testing framework. Its usage is actually similar to Jest, but its performance is much better than Jest. It also provides good ESM support. Moreover, for projects using Vite as a build tool, a benefit is that they can share the same configuration file, vite.config.js. Therefore, this project will use Vitest as the test framework.

## Installation

Since the front-end component library we are testing runs on the browser, we need to install happy-dom additionally. We also need to install the test coverage display tool c8.

```bash
pnpm add vitest happy-dom c8 -D -w
```

## Configuration

As mentioned above, Vitest can be configured directly in vite.config.ts, so we go to components/vite.config.ts to make a related configuration for Vitest (the triple-slash directive tells the compiler to include additional files during the compilation process).

```js
/// <reference types="vitest" />
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue"
...
export default defineConfig(
    {
        ...
        test: {
            environment: "happy-dom"
        },

    }
)
```

Then we add two commands in package.json, 'vitest' and 'vitest run --coverage', which are for performing unit tests and viewing unit test coverage respectively.

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

Now we can use Vitest for testing. When executing the 'test' command, Vitest will execute files with the pattern '**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'. Here, we uniformly name our test files in the form of '**/*.{test}.ts' and place them in the '__tests__' directory of each component.

For example, create a new directory '__tests__/button.test.ts' under the 'button' directory, and then write a simple test code to see how it works.

```js
import { describe, expect, it } from 'vitest';

describe('hellostellarnovaui', () => {
  it('should be hellostellarnovaui', () => {
    expect('hello' + 'stellarnovaui').toBe('hellostellarnovaui');
  });
});
```

Then, by executing 'pnpm run test' in the 'components' directory, we can see that our test has passed.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wt7ff1mngwh23v8yy6wg.png)

Then, by executing 'pnpm run coverage', we can see our test coverage.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qhn7ovqmy8h8dmv7uqmb.png)

The meaning of each field is as follows:

- '%stmts' is statement coverage: Has every statement been executed?

- '%Branch' is branch coverage: Has every 'if' code block been executed?

- '%Funcs' is function coverage: Has every function been called?

- '%Lines' is line coverage: Has every line been executed?

## How to Test Components?

Above, we just simply tested a string addition, but what we need to test is a component library. So, how do we test whether our components meet the requirements?

Since our project is a Vue component library, we can install the test library recommended by Vue, `@vue/test-utils`.

```bash
pnpm add @vue/test-utils -D -w
```

Then we modify button.test.ts, we are going to test the slot of the Button component.

```js
import { describe, expect, it } from 'vitest';

import { mount } from '@vue/test-utils';
import button from '../button.vue';
// The component to test
describe('test button', () => {
  it('should render slot', () => {
    const wrapper = mount(button, {
      slots: {
        default: 'stellarnovaui'
      }
    });

    // Assert the rendered text of the component
    expect(wrapper.text()).toContain('stellarnovaui');
  });
});
```

`@vue/test-utils` provides a 'mount' method, we can pass in different parameters to test whether the component meets our expectations. For example, the meaning of the above test code is: pass in the button component, and set its default slot to 'stellarnovaui'. We expect the text 'stellarnovaui' to be displayed when the page is loaded. Obviously, our button component has this feature, so when we execute 'pnpm run test', this test will pass.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jn95ki9g91w1vexwt097.png)

If we want to test how the Button component displays a certain style when a type is passed in, we can write it like this.

```js
import { describe, expect, it } from 'vitest';

import { mount } from '@vue/test-utils';
import button from '../button.vue';
// The component to test
describe('test button', () => {
  it('should render slot', () => {
    const wrapper = mount(button, {
      slots: {
        default: 'stellarnovaui'
      }
    });

    // Assert the rendered text of the component
    expect(wrapper.text()).toContain('stellarnovaui');
  });
  it('should have class', () => {
    const wrapper = mount(button, {
      props: {
        type: 'primary'
      }
    });
    expect(wrapper.classes()).toContain('sn-button--primary');
  });
});
```

The meaning of this test is: when the type we pass in is 'primary', we expect the class name of the component to be 'sn-button--primary'. Obviously, this one can also pass. At the same time, you will find that the test we just started has automatically updated itself, indicating that Vitest has a hot update feature.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4r6ce5syoahjstpnc0jn.png)

For those interested in more features of `@vue/test-utils`, you can click on [@vue/test-utils](https://test-utils.vuejs.org/guide/) to view the official documentation.

The final source code: https://github.com/markliu2013/StellarNovaUI