Good Tests for Vue Applications

Share this post
Chapter 3.1.1: Basic test setup with Cypress and Vitest: directory structure
goodvuetests.substack.com

Chapter 3.1.1: Basic test setup with Cypress and Vitest: directory structure

This chapter will focus on setting up a standard Vue project with Vite and adding a basic configuration for testing. First we start with the directory structure.

Markus Oberlehner
May 16
Share this post
Chapter 3.1.1: Basic test setup with Cypress and Vitest: directory structure
goodvuetests.substack.com

Disclaimer: You are reading an early version of the text! The final version of the book will be revised and may contain additional content.


We'll start with the basics before we dig deeper into what additional tools and configuration steps we need to meet all the requirements for a perfect setup. This chapter will focus on setting up a standard Vue project with Vite and adding a basic configuration for testing. For this purpose, we will install Cypress and Vitest. For both frameworks, we will place all related files, such as configuration files and plugins, in a central location in the file tree of our project. In the first step, we assume that we will use Cypress exclusively for application tests and Vitest only for unit tests. Later in the book, we will extend the test setup to use both frameworks for both types of tests. Likewise, we will build on the base setup and extend it to cover the three principles defined earlier in the best possible way.

So let’s start with the initial setup: We will install and configure the required dependencies step by step. You can take a shortcut and download the demo project from the following URL: https://github.com/maoberlehner/perfect-test-setup/tree/base-setup (paid subscribers and book owners only; please get in touch with me if this is you and you want access). However, I still recommend at least skimming the upcoming section, as we will already take a closer look at some crucial aspects during the basic setup as well.


Disclaimer: I created the following examples in this book using Node v16, npm v8, and the current (as of May 7, 2022) versions of the respective npm dependencies. Depending on when you read this, some adaptations may be necessary. I ask for your understanding.


We use a Vite project with TypeScript as the basis for our perfect test setup:

# Use your preferred project name instead of `good-tests`
npm create vite@latest good-tests -- --template vue-ts
cd good-tests
npm install

After the initial setup of our brand new Vue application, we can proceed to install the test frameworks of our choice and a couple of other dependencies:

npm install --save-dev cypress vitest happy-dom c8 @vue/test-utils

In the first step, we will use cypress for application tests, and vitest is our preferred tool for unit tests. Where by unit tests, we mean tests for JavaScript classes and functions as well as Vue component tests. We are using the happy-dom package in combination with Vitest to simulate a DOM environment under Node.js. c8 is a package that allows us to measure how well our code is covered by tests. Finally, the @vue/test-utils help us to test Vue components.

Now we can start configuring the test frameworks. By default, the configuration files for the two frameworks are located directly in an application's root directory. However, this quickly becomes messy the more configuration files accumulate in a project. I, therefore, prefer to move the configuration files into the tests directory. And within the tests directory, we also want to move all files that are specific to a particular framework into a subdirectory again. To do this, we create separate folders for Cypress and Vitest, respectively. Doing so makes it much easier for us to swap test frameworks in the future if necessary. We can find all files related to a particular test framework in the corresponding folder. For example, if we decide to switch from Vitest back to Jest, we only need to delete the corresponding folder, and we can be sure that all traces of Vitest are gone from our codebase.

├─ ...
├─ tests
│  ├─ driver
│  │  ├─ cypress
│  │  │  ├─ plugins
│  │  │  │  └─ index.ts
│  │  │  ├─ support
│  │  │  │  └─ index.ts
│  │  │  ├─.gitignore
│  │  │  ├─ cypress.config.json
│  │  │  └─ tsconfig.json
│  │  └─ vitest
│  │  │  ├─.gitignore
│  │  │  └─ vitest.config.json
│  ├─ specs
│  └─ tsconfig.json
└─ ...

Above, we see the file tree of the tests folder in our project. Let's start with something simple: tests/driver/cypress/plugins/index.ts.

// tests/driver/cypress/plugins/index.ts
export default () => {
  // configure plugins here
};

This file serves simply as a placeholder for now. When we are ready, we can add plugins here. Similarly, with tests/driver/cypress/support/index.ts, we can leave this file empty for now. All that matters is that these two files exist because we also reference them in cypress.config.json. Later we will come back to these files.


This article is a part of my book about writing good tests for Vue applications. Every two weeks, I publish a new article. Become a free or paid subscriber to support my work and never miss an article.


The file tests/driver/cypress/.gitignore contains only two lines:

# tests/driver/cypress/.gitignore
screenshots
videos

Cypress automatically creates screenshots and videos when tests fail. Having a screenshot or a video can be enormously helpful when figuring out why a test is failing. However, we don't want these automatically generated files in our Git repository. We can prevent this by adding these two lines to the .gitignore file.

Next, let's look at the cypress.config.json file:

{
  "baseUrl": "http://localhost:3000",
  "componentFolder": "src/components",
  "downloadsFolder": "tests/driver/cypress/downloads",
  "fixturesFolder": "tests/driver/cypress/fixtures",
  "integrationFolder": "tests",
  "pluginsFile": "tests/driver/cypress/plugins/index.ts",
  "screenshotsFolder": "tests/driver/cypress/screenshots",
  "supportFile": "tests/driver/cypress/support/index.ts",
  "videosFolder": "tests/driver/cypress/videos",
  "testFiles": "**/specs/**/*.spec.ts"
}

Essentially, this is where we tell Cypress where to find all mandatory folders and files. If you decide to use a different folder structure, this is the place to adjust the paths accordingly.

Now we move on to the tests/driver/cypress/tsconfig.json file. Here we can make settings for TypeScript that only take effect in this directory and its subdirectories.

{
  "extends": "../../../tsconfig.json",
  "compilerOptions": {
    "types": [
      "cypress"
    ]
  }
}

We want to inherit all the settings from our global configuration file. Therefore we use the extends option. In addition, all Cypress relevant types for TypeScript should be available in this directory. We achieve this with the compilerOptions setting. Of course, we could also apply this configuration in the global tsconfig.json file. But this way, the Cypress-specific configuration is cleanly separated from the rest of our application.

In the global tsconfig.json, we explicitly ignore the tests folder! This is because we want to strictly separate the TypeScript configuration of our application code from the test code. Therefore we need a second tsconfig.json file: tests/tsconfig.json.

{
  "extends": "../tsconfig.json",
  "include": ["**/*.ts", "**/*.d.ts"],
}

Here we extend the tsconfig.json file from the root directory and include all relevant TypeScript files in the tests directory and its subdirectories.

There is also a .gitignore file in the vitest driver folder. In it, we specify that the coverage folder, which Vitest creates when we measure code coverage, should not be added to our Git repository.

# tests/driver/vitest/.gitignore
coverage

Now the only thing missing is the vitest.config.ts file. It contains all settings for the Vitest framework.

// tests/driver/vitest/vitest.config.ts
import { defineConfig } from 'vitest/config';

import viteConfig from '../../../vite.config';

export default defineConfig({
  ...viteConfig,
  test: {
    environment: `happy-dom`,
    include: [`./src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}`],
    coverage: {
      reportsDirectory: `./tests/driver/vitest/coverage`,
    },
  },
});

Since Vitest uses Vite under the hood, we can conveniently reuse the Vite configuration of our project. This is a massive advantage over Jest and other frameworks. All the plugins and settings necessary to bundle our application are thus automatically available in our test runner. We only need to specify happy-dom as environment and use the include option to specify where Vitest should look for test files.

A major goal of our test setup is to be able to interchange the technologies used as conveniently as possible without requiring a complete rewrite of all tests. Moving all files concerning a particular test framework into a separate folder goes a long way towards achieving this goal.


So much for the basic initial setup of Cypress and Vitest. In the following article, we will add a watch mode to Cypress, localize Cypress helper functions, and, ultimately write our first two tests.

What is your experience with setting up test frameworks? Do you have a trick up your sleeves that you want to share with us?

Leave a comment

Share this post
Chapter 3.1.1: Basic test setup with Cypress and Vitest: directory structure
goodvuetests.substack.com
Comments

Create your profile

0 subscriptions will be displayed on your profile (edit)

Skip for now

Only paid subscribers can comment on this post

Already a paid subscriber? Sign in

Check your email

For your security, we need to re-authenticate you.

Click the link we sent to , or click here to sign in.

TopNewCommunity

No posts

Ready for more?

© 2022 Markus Oberlehner
Privacy ∙ Terms ∙ Collection notice
Publish on Substack Get the app
Substack is the home for great writing