DEV Community

Cover image for Add testing to Vite
Gábor Soós
Gábor Soós

Posted on • Updated on

Add testing to Vite

Vite is the brand new development server created by Evan You. It's framework agnostic and incredibly fast thanks to native ES Modules instead of bundling. Vite has a starter template for Vue applications. The template has the tooling for development and production deployment; only one is missing: testing. This tutorial shows you how to add unit and end-to-end testing to a newly generated Vue 3 Vite project.

Project setup

Let's start by creating a Vite project from scratch.

npm init @vitejs/app my-vite-app --template vue-ts
Enter fullscreen mode Exit fullscreen mode

The above command creates a Vue 3 Typescript application into the my-vite-app folder. The folder structure will look like this.

Folder Structure

We have a HelloWorld component in the src/components/HelloWorld.vue file that displays the header on the page "Hello Vue 3 + TypeScript + Vite". This component receives the header text a prop input named msg. We'll write a test against this component whether it displays the same message as what we give as input.

Application

Unit test

As mentioned in the headline, the Vite template doesn't include any test runner; we have to choose one. The Jest test runner is a good choice if we want a simple and fast setup as it gives us everything that we need: a test runner that executes the tests, an assertion library with which we can assert for the outcome and a DOM implementation where Vue components can be mounted.

Here is our first unit test placed next to the HelloWorld.vue component file.

// src/components/HelloWorld.spec.ts
import { mount } from '@vue/test-utils'
import HelloWorld from './HelloWorld.vue'

describe('HelloWorld', () => {
  it('should display header text', () => {
    const msg = 'Hello Vue 3'
    const wrapper = mount(HelloWorld, { props: { msg } })

    expect(wrapper.find('h1').text()).toEqual(msg)
  })
})
Enter fullscreen mode Exit fullscreen mode

The test uses the Vue Test Utils library, the official unit test helper library. With its help, we can mount a single component to the DOM and fill the input parameters, like its props.

We feed the "Hello Vue 3" text to the component and check the outcome through the components wrapper object. If the header element has the same text as the input, the test passes. But how do we run this test?

I've mentioned Jest and Vue Test Utils, but we haven't installed any packages.

npm install jest @types/jest ts-jest vue-jest@next @vue/test-utils@next --save-dev
Enter fullscreen mode Exit fullscreen mode

By default, Jest doesn't understand Vue and Typescript files, so we need to transpile them before and pass the transpilation step as configuration (jest.config.js). We need the next version for multiple packages because they are the only ones that support Vue 3.

// jest.config.js
module.exports = {
  moduleFileExtensions: [
    'js',
    'ts',
    'json',
    'vue'
  ],
  transform: {
    '^.+\\.ts$': 'ts-jest',
    '^.+\\.vue$': 'vue-jest'
  }
}
Enter fullscreen mode Exit fullscreen mode

We are two little steps away from running and seeing passing tests. First, we have to add the type definition of Jest to the config.

// tsconfig.json
{
  "compilerOptions": {
    ...
    "types": ["vite/client", "@types/jest"],
    ...
  },
  ...
}
Enter fullscreen mode Exit fullscreen mode

Finally, add the script to package.json, and after that, we can run the tests with npm test.

// package.json
{
  ...
  "scripts": {
    ...
    "test": "jest src"
  },
  ...
}
Enter fullscreen mode Exit fullscreen mode

And here it is the result of our first unit test, beautiful green, and passing.

Jest Output

E2E test

While unit tests are good for checking smaller bits of code, end-to-end tests are really good at checking the application as a whole in the browser. Vue CLI comes with built-in support for Cypress, an end-to-end test runner. We'll also use Cypress for this purpose.

// e2e/main.spec.ts
describe('Main', () => {
  it('should display header text', () => {
    cy.visit('/')
    cy.contains('h1', 'Hello Vue 3 + TypeScript + Vite')
  })
})
Enter fullscreen mode Exit fullscreen mode

Cypress provides a global object cy that can interact with the browser. It can visit certain pages (visit) and check the content of elements defined by a selector (contains). In the above test, we navigate to the main page and check for the correct header text.

It's time to install the necessary packages to run the test.

npm install cypress start-server-and-test --save-dev
Enter fullscreen mode Exit fullscreen mode

Next to Cypress, we've installed a utility library called start-server-and-test. This utility library can start the development server, wait until it responds to the given URL, and then runs the Cypress tests. In the end, it stops all running processes during the cleanup phase.

Cypress doesn't know where the test files are located and the base URL of the application, we have to tell it with a configuration file.

// cypress.json
{
  "baseUrl": "http://localhost:3000",
  "integrationFolder": "e2e",
  "pluginsFile": false,
  "supportFile": false,
  "video": false
}
Enter fullscreen mode Exit fullscreen mode

Manually declared types within our Typescript configuration needs manual work again: add Cypress types to the list.

// tsconfig.json
{
  "compilerOptions": {
    ...
    "types": ["vite/client", "@types/jest", "cypress"],
    ...
  },
  ...
}
Enter fullscreen mode Exit fullscreen mode

One piece is left to create the script command in package.json to run the tests. We use the start-server-and-test package with three command-line arguments:

  • dev: the npm script name which runs the development server
  • http-get://localhost:3000: the URL where the development server is available
  • cypress: the npm script name which runs the end-to-end tests
// package.json
{
  ...
  "scripts": {
    ...
    "test:e2e": "start-server-and-test dev http-get://localhost:3000 cypress",
    "cypress": "cypress run"
  },
  ...
}
Enter fullscreen mode Exit fullscreen mode

If we run the above script, we get passing shiny green end-to-end test results.

Cypress Output

Summary

Vite is a great development server, but its templates lack testing solutions. We have to add them manually. Jest and Cypress offer straightforward scenarios to fill in this gap. We can also swap them to similar libraries like Mocha, Jasmine, and Puppeteer. After this article, I hope the lack of out-of-the-box testing with Vite doesn't hold back anyone from using it.

If you don't want to set up everything manually, you can use my Vue 3 Playground as a starter.

Cover photo by Liam Shaw

Top comments (22)

Collapse
 
soetz profile image
Sœtz - Simon Lecutiez • Edited

Hey! Thanks a load for this, but it seems it doesn’t work for me… The error I got says Cannot find module './xxx.vue' or its corresponding type declarations.. I tried searching for ways to fix this but couldn’t understand what’s wrong. :( It’s such a shame because Vite was a nice experience until then.

Collapse
 
webmaplee profile image
webmapLee

just use vue-jest@next don't use vue-jest/next or vue3-jest ,it's a issues of Version compatibility, then config in jest.config.js as follow:

module.exports = {
moduleFileExtensions: ['js', 'ts', 'json', 'vue'],
transform: {
'^.+\.ts$': 'ts-jest',
'^.+\.vue$': '@vue/vue3-jest',
},
testEnvironment: 'jsdom',
// transformIgnorePatterns: ['node_modules/(?!variables/.*)'],
}

remember set the testEnvironment to 'jsdom'

Collapse
 
odinkraa profile image
xl ama slim fit

I had the same problem, got it to working by downing
jest version to 26.6.3
ts-jest to 26.5.6

here is my package.json

{
"name": "vite-testing",
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"serve": "vite preview",
"test": "jest src"
},
"dependencies": {
"vue": "^3.2.13"
},
"devDependencies": {
"@types/jest": "^26.0.2",
"@vitejs/plugin-vue": "^1.9.0",
"@vue/test-utils": "^2.0.0-rc.15",
"jest": "^26.6.3",
"ts-jest": "^26.5.6",
"typescript": "^4.4.3",
"vite": "^2.5.10",
"vue-jest": "^5.0.0-alpha.10",
"vue-tsc": "^0.3.0"
}
}

Collapse
 
odinkraa profile image
xl ama slim fit

the problem now is it's passing while it shouldn't lol

Collapse
 
lemredd profile image
lemredd

This also worked for me

Collapse
 
webmaplee profile image
webmapLee

i have same problem, just use @vue/vue3-jest

Collapse
 
geminii profile image
Jimmy

I got the same error. How to fix this ?

Collapse
 
sonicoder profile image
Gábor Soós

Can you share code alongside the error message?

Collapse
 
soetz profile image
Sœtz - Simon Lecutiez

I tried it again, and I think I got it right this time. Thanks!

Collapse
 
keithn profile image
Keith Nicholas

I tried this from a freshly created vite/vue ts project and just ended up with

import { mount } from '@vue/test-utils';
^^^^^^

SyntaxError: Cannot use import statement outside a module

  at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1350:14)
Enter fullscreen mode Exit fullscreen mode
Collapse
 
nvh95 profile image
Hung Viet Nguyen

If any of you guys setting for Vue Javascript, or having .js file in theproject, the jest.config.js file should look like this to avoid the error SyntaxError: Cannot use import statement outside a module.

// jest.config.js
module.exports = {
  moduleFileExtensions: [
    'js',
    'ts',
    'json',
    'vue'
  ],
  transform: {
-    '^.+\\.ts$': 'ts-jest',
+    '^.+\\.(js|ts)$': 'ts-jest',
    '^.+\\.vue$': 'vue-jest'
  }
}
Enter fullscreen mode Exit fullscreen mode

@vuesomedev Can you update the post to include .js files to use ts-jest as well. Since typecsript.json might have option "allowJs": true.

Collapse
 
cindrmon profile image
Cindr Mon

hio! i don't know what im doing wrong, but its giving me this error: TypeError: Cannot destructure property 'config' of 'undefined' as it is undefined. Have you encountered this error, and how do you fix it? Thanks

Collapse
 
victorneves profile image
Victor Neves • Edited

Hey
It's not working for me.
I'm dealing with the same problem that was already mention before, and I tried to follow what they did to fix but didn't worked for me.

Alt text
Alt text

Collapse
 
jaiko86 profile image
jaiko86

Worked like a charm.

Collapse
 
patrikbird profile image
PatrikBird

thanks for bringing vite back to my mind and all the nice vue content. keep it up!

Collapse
 
sonicoder profile image
Gábor Soós

thanks, planning to write about vue cli -> vite migration

Collapse
 
mattdeacalion profile image
Matt Deacalion

I'm currently in the process of migrating to Vite, that would be very helpful!

Collapse
 
modularcoder profile image
Gevorg Harutyunyan

Great article, thanks!

Collapse
 
zynth17 profile image
Christopher Reeve

how to do testing using the testing library with vite?

Collapse
 
quantuminformation profile image
Nikos
Collapse
 
geewhizbang profile image
Geoffrey Swenson

It isn't clear where either jest.config.js or cypress.json should be saved. Is it in src, or at the root of the project?

Collapse
 
geewhizbang profile image
Geoffrey Swenson

I have a component that has a router-link. The Vue2x methods of injecting the router into the test don't work. How do you do this with Vue3?