Configuring vitest and testing library to work together - step by step guide
Tags: vue, webdev, testing
Published: 2023-02-02 22:10:00

The javascript ecosystem provides a variety of tools and frameworks that sometimes require some configuration dancing to work together.

Jest has been the dominating player for running javascript tests, it provides a end-to-end experience in regards of features, for example, you don't need to configure a test runner and also the assertion library. Even though, such thing is possible to do if needed.

Jest works across libraries and frameworks, React packs Jest into automatically when creating a project using Create-React-App.

Besides, Jest also works with applications that are run in the server side.

Such popularity was questioned by vitest, an alternative to Jest, that is focused in speed.

Vitest was born from vuejs ecosystem and rapidly started to evolve to other communities, such evolution, leads to a friction when migrating or even when trying to make tools to work together.

In Jest, testing-library works automatically, no much effort needs to be done. On the other hand, trying to combine, testing library and vite, might bring some challenges.

The aim here is to go step-by-step to configure vite and testing-library in an environment that uses typescript.


## The project

To start with, let's create a project with [create-vue](https://github.com/vuejs/create-vue), executing the following command should create a brand new project:

```sh
npm create vue@3 create && \
  cd vitest-testing-library && \
  npm i
```

This command will issue an interactive terminal to fill in some relevant information for the project, the following options were used:


![Options used to create the vuejs application through create-vue](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k8o4g7ncrkevakpqik46.png)

Using create-vue jump a few steps in the way to configure vuejs and vitest. Setting up vitest in an already existing
vuejs application requires a different approach.

To make sure that the requirements are in place before moving to the next section run the test suite with the following npm script:

```sh
npm run test:unit
```

The output should be green, indicating that the test passed, as the following image depicts:

![Output vitest execution](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9c0glxpir7x87wfgee82.png)

## Why testing library

If you are like me, the ideal test is the one that is not coupled with my production code allowing me to refactor and improve the code as much as I want without even worrying about the test.

Opposed to testing library, vue testing utils expose the internals of a component, and this scenario is depicted in the test file that comes with the project we just created.

```ts
describe('HelloWorld', () => {
  it('renders properly', () => {
    const wrapper = mount(HelloWorld, { props: { msg: 'Hello Vitest' } })
    expect(wrapper.text()).toContain('Hello Vitest')
  })
})
```
Anytime I change the props, to something else that is not *msg* I will have to update my test file, which is not desired, I haven't changed any behavior to break my tests, such change should not be needed.

Testing library allow the test to reflect what the user will do when interacting with the application, rather then focusing on component implementation such as props.

Testing library became popular within react ecosystem, but it was adopted by others as its benefits and philosophy supports evolution of applications as the one described.

## Installing testing library

The version 3 of vuejs requires installing a different package from vuejs 2 as the official documentation depicts. As in the previous step was used vue 3, the following command is needed in order to install testing library:

```sh
npm install --save-dev @testing-library/vue
```

Once Installed, the library will allow us to convert the previous test case to something to the following:

```ts
it('renders properly', () => {
  const {getByText} = render(HelloWorld)
  expect(getByText('Hello Vitest')).toBeInTheDocument();
})
```

Doing so and trying to execute the test suite will lead to a different problem, now, vitest complains that it cannot understand *toBeInTheDocument*, as the following image depicts:

![Error faced by vitest when using a matcher that is not 
supported](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/db2tmqim6w5t5ponfcl6.png)

Which makes sense. This is a matcher from Jest that vitest does not support, fixing that was shown already by
[this blog post](https://markus.oberlehner.net/blog/using-testing-library-jest-dom-with-vitest), which installs the jest-dom package and extends the vitest matchers.

### Error: Invalid Chai property: toBeInTheDocument

The next step to fix this issue is to install the package [@testing-library/jest-dom]() that adds the matchers used
by jest in our project, to accomplish that the following command should be executed:

```sh
npm install --save-dev @testing-library/jest-dom
```

Then, the matcher can be exposed to vitest. Exposing the matchers to vitest happens through extending the vitest matchers, Markus recommended to create a file named setup.ts* under the folder test, inside *src*, with the following content:

```ts
import matchers from '@testing-library/jest-dom/matchers';
import { expect } from 'vitest';

expect.extend(matchers);
```

With this file in place, the next step is to inform vitest to use this file and load that up before the test run. Informing
vitest about the *setup.ts* file is as simple as adding it in the configuration file *vite.config.ts*.

```ts
import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vitest'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  },
  // ===============================================
  // ===============================================
  // Add the live above to the configuration, it will inform vitest to use the file.
  test: {
    setupFiles: ['test/setup.ts']
  }
})
```

### TS2345 'test' does not exist in type 'UserConfigExport' - Issues with type script

You might also face an issue with types in typescript when trying to add the key *test* in the configuration,
the error is similar to the following:

```
TS2345: Argument of type '{ plugins: Plugin_2[]; resolve: { alias: { '@': string; }; }; test: { setupFiles: string[]; }; }' is not assignable to parameter of type 'UserConfigExport'.   Object literal may only specify known properties, and 'test' does not exist in type 'UserConfigExport'.
```

The fix is to replace the typed configuration from vite to vitest:

```ts
import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vitest/config' // <!------ This is the fix
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  },
  // ===============================================
  // ===============================================
  // Add the live above to the configuration, it will inform vitest to use the file.
  test: {
    setupFiles: ['test/setup.ts']
  }
})
```
With that in place the test should be back to green again, the configuration, in the execution point of view is done.

### TS2339: Property 'toBeInTheDocument' does not exist on type 'Assertion ' - Fixing assertions type

The last bit of this configuration left is to share the types that jest has to use with testing library, this is needed
because at this point in time, the project should be giving the following error when trying to build with *npm run build* or opening it in an IDE or text editor.

The following image depicts the error when trying to build:

![Typescript error building the application](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1883aui0p52pgi7qz8kn.png)

Fortunately, the trick to fix this issue is adding the types that jest also uses.

To start fixing this, run the following command in the terminal:

```sh
npm i --save-dev @types/testing-library__jest-dom 
```

With the types installed, the next step is to share them with vitest. The file that is responsible for that is in the root of the project and is named *tsconfig.vitest.json*, opening the file, should give the following content:

```json
{
  "extends": "./tsconfig.app.json",
  "exclude": [],
  "compilerOptions": {
    "composite": true,
    "lib": [],
    "types": ["node", "jsdom"]
  }
}
```

The key that we are interested in is the last one, under *compileOptions* named *types*. Types is the place that we can give vitest the hint of the types we are using. The step missing is the one to add the type *@types/testing-library__jest-dom* there.

In the end, the file *tsconfig.vitest.json* should be as the following:

```json
{
  "extends": "./tsconfig.app.json",
  "exclude": [],
  "compilerOptions": {
    "composite": true,
    "lib": [],
    "types": ["node", "jsdom", "@types/testing-library__jest-dom"] // <------ we added this last type
  }
}
```

Running the command *npm run build* again should give the desired output, building the application without problems. The following image depicts the successful run of the build command.

![Success output running the build command](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vpndsfjko6vhtdoay9ix.png)