Get the Most of Your Testing: Combining Vitest and Testing Library for Maximum Efficiency

Last updated Jun 30, 2023 Published Jan 14, 2023

The content here is under the Attribution 4.0 International (CC BY 4.0) license

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 vitest, might bring some challenges. The aim here is to go step-by-step to configure both of them in an environment that uses typescript.

The project

To start with, let’s create a project with create-vue, executing the following command should create a brand new project:

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

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:

npm run test:unit

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

Output vitest execution

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.

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. It 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 the library:

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:

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

Which makes sense. This is a matcher from Jest that vitest does not support, fixing that was shown already by this blog post, 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:

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:

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.

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 vitest root to the one under vitest/config:

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

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:

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:

{
  "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:

{
  "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