10 min read

Why bother migrating to Vitest?

Vitest is a modern test runner with improved perfomance and developer experience comapred to many other existing alternatives, which explains its growing popularity.

But, the manual migration from Mocha.js to Vitest could take a considerable amount of time, especially when you have a lot of test suites.

What if there were code modification bots that could automate the entire migration process?

Codemod.com: The end-to-end platform for code automation at scale

Creating the code modification bots, also known as codemods, requires knowledge of the compiler theory, namely navigating around the abstract syntax trees. Interestingly, each compiler and transpiler has its own tree structure, which makes the creation of automation agents difficult. Therefore, if you want to build the agents proficiently, you need to know such structures by heart.

This is why we created the Codemod.com platform to build, test, distribute, and execute codmods for codebases of any size.

  • Build: Leverage the latest LLMs in Codemod Studio for free to instantly create and test codemods using the jscodeshift API, a library designed for manipulating abstract syntax trees in JS/TS. The studio offers one-click integration for VS Code and CLI to run the auto-generated codemods.
  • Share: Add your codemods to Codemod.com's private or public distributed registries, making them available to colleagues or the community.
  • Run: Discover and run codemods with a single click or CLI command, tweak changes as needed, and iteratively improve them by providing feedback to Codemod.com AI or the community.
  • Manage: Codemods alone are not sufficient for successful migrations in large codebases and teams. Orchestrating the changes made by codemods across multiple teams requires a platform that can integrate with the existing tools of each software team. These features, which are only needed by large teams, fall under Codemod.com's enterprise offerings.

With that in mind, let's go over how we leveraged our platform to migrate from Mocha to Vitest.

Mocha to Vitest migration with Codemod.com

1. Build the codemod

To build a codemod with Codemod Studio, you only need two code snippets: one before the transformation and one after.

Imagine we have an existing Mocha.js-compatible test file with the following code, which we place in the “before” snippet:

1import type { Context } from 'mocha';
2import { expect } from 'chai';
3
4describe('Test Suite 1', function (this) {
5 beforeEach(() => {
6 doAThing();
7 });
8
9 it('addition', () => {
10 expect(1 + 1).to.equal(2);
11 });
12
13 afterAll(() => {
14 doBThing();
15 });
16});

With Vitest, we would write the test file in the following way, which we place in the “after” snippet:

1import { describe, it, expect, beforeEach, afterAll } from 'vitest';
2
3describe('Test Suite 1', () => {
4 beforeEach(() => {
5 doAThing();
6 });
7
8 it('addition', () => {
9 expect(1 + 1).to.equal(2);
10 });
11
12 afterAll(() => {
13 doBThing();
14 });
15});

As you can see, in Mocha.js describe, beforeEach, it and afterAll functions exist in the global scope. Additionally, we chose to use the expect function from the chai library explicitly for test assertions. In Vitest, we need to import all of these functions from the vitest package.

Instead of writing the jscodeshift codemod ourselves, we can automate it using the Codemod Studio.

Within a few iterations with the AI helper, we got the following codemod:

1import type { FileInfo, API, Options } from 'jscodeshift';
2export default function transform(
3 file: FileInfo,
4 api: API,
5 options?: Options,
6): string | undefined {
7 const j = api.jscodeshift;
8 const root = j(file.source);
9
10 // Helper function to preserve leading comments
11 function replaceWithComments(path, newNode) {
12 // If the original node had comments, add them to the new node
13 if (path.node.comments) {
14 newNode.comments = path.node.comments;
15 }
16
17 // Replace the node
18 j(path).replaceWith(newNode);
19 }
20
21 // Check if beforeEach and afterAll are used in the code
22 const isBeforeEachUsed = root.find(j.Identifier, { name: 'beforeEach' }).size() > 0;
23 const isAfterAllUsed = root.find(j.Identifier, { name: 'afterAll' }).size() > 0;
24
25 // Find all import declarations
26 root.find(j.ImportDeclaration).forEach(path => {
27 // Check if import source is 'chai'
28 if (path.node.source.value === 'chai') {
29 // Replace 'chai' with 'vitest'
30 path.node.source.value = 'vitest';
31 // Add 'describe', 'it', 'expect', 'beforeEach', 'afterAll' to import specifiers conditionally
32 const importNames = ['describe', 'it', 'expect'];
33 if (isBeforeEachUsed) importNames.push('beforeEach');
34 if (isAfterAllUsed) importNames.push('afterAll');
35 path.node.specifiers = importNames.map(name =>
36 j.importSpecifier.from({ imported: j.identifier(name), local: j.identifier(name) })
37 );
38 // Replace the node with the new node, preserving comments
39 replaceWithComments(path, path.node);
40 }
41 });
42
43 // Find all function declarations
44 root.find(j.FunctionExpression).forEach(path => {
45 // Check if function has 'this' as parameter
46 if (path.node.params.some(param => param.type === 'Identifier' && param.name === 'this')) {
47 // Remove 'this' from parameters
48 path.node.params = path.node.params.filter(param => !(param.type === 'Identifier' && param.name === 'this'));
49 // Replace function declaration with arrow function expression
50 const arrowFunction = j.arrowFunctionExpression.from({
51 params: path.node.params,
52 body: path.node.body,
53 async: path.node.async,
54 });
55 // Replace the node with the new node, preserving comments
56 replaceWithComments(path, arrowFunction);
57 }
58 });
59
60 return root.toSource();
61}

The generated codemod served as a base for the final one called mocha/vitest/migrate-tests. We refined it manually based on happy-path tests and executed the codemod directly on our own projects, which we will describe in the following section.

We also built codemod mocha/vitest/migrate-configurations using Codemod.com's engine (called filemod) as jscodeshift does not allow creating and removing files.

The filemod removes all Mocha.js configuration files, drops the library from all package dependencies, and adds Vitest instead.

Having both a jscodeshift codemod and a filemod left us with two code automation agents to execute sequentially. To facilitate that, we introduced the concept of a recipe. Our recipes contain information for the codemod runners to execute specific agents one by one and combine the results.

We called the recipe for the described migration mocha/vitest/recipe which can be discovered, shared, and run like any other codemods.

2. Run the codemod

1npx codemod mocha/vitest/recipe

We ran the recipe over 3 of our own repositories:

  1. codemod-com/filemod
  2. codemod-com/codemod-engine-node
  3. codemod-com/codemod-registry

The first two had a handful of Mocha.js test suites and we migrated them first to spot problems early in the development. Once the codemod was ready and accurate, we merged them into the Codemod Registry. Once codemods are published to the registry, they automatically become accessible to the community via Codemod.com VSCode Extension or the Codemod.com CLI.

And shown below, with the help of our codemod, we automated the bulk of the migration for over 60 test files spread across the entire Codemod Registry monorepo.

We repeated the same steps for the Codemod Engine Node. During testing, we found out Vitest does not transpile TypeScript files if we use them as entry points for thread workers. This functionality used to work with Mocha.js supported by the ts-node/esm dependency. We replaced the path to the TS file with a path to the built ESM files to restore the feature.

3. Final tweaks

When migrating the Codemod Registry, we found unhandled cases, like:

  • the usages of the Context type from Mocha.js,
  • the usages of the this variable in test bodies.

We could fix the former problem by putting the existing codemod into the Codemod Studio and asking the AI helper to fix it. With the latter, we fixed the test bodies manually due to the small number of such occurrences.

Since the Codemod.com CLI can execute local codemods, we used it to see if the changes to the codemod resulted in the correct transformation of the codebase in question. This speeded up the development process tremendously.

The impact

With the help of the recipe, we were able to migrate three repositories in a short amount of time. We reused the same code within the recipe for all the projects. Now, other developers may leverage our work as well.

If we were to manually migrate the Codemod Registry alone, we would have to:

  1. delete the 60+ existing Mocha.js configuration files (1 minute for each),
  2. remove Mocha.js and add Vitest dependencies in 60+ package files (1 minute for each),
  3. drop Mocha.js types and put the Vitest import statements in 60+ tests files (2 minutes for each).

It would have taken around 4 hours of mundane work to perform the migration without ensuring we had not introduced any mistakes.

After ensuring the proper quality of the code automation agents, we executed the recipe that transformed over 200 files instantly. We removed all Mocha.js configuration files and thus reduced the complexity of the test suites. Our platform made it all available at the push of a button.

The future of automated migrations

Thanks to generative AI, the volume and velocity of software components will grow exponentially. Without a given infrastructure, these components will collapse under their own weight and cannot keep up with the pace of the other components they rely on.

Manual migrations of massive codebases become increasingly intractable, creating new opportunities for dedicated platforms specifically designed for autonomous code evolution at scale.

We envision a future where everyone is a "developer," creating delightful, performant, and secure digital experiences. Human developers will focus only on tasks that require innovation and spark their interest, while any undifferentiated tasks are managed by AI and its surrounding infrastructure.

Code automation platforms, such as Codemod.com, will serve as an indispensable part of any company's technology stack, transforming lifeless software into a living entity that evolves autonomously. Software of the future will be orders of magnitude larger than what we have today, mostly created by AI developers. It will be understandable and controllable by humans and give rise to new experiences in life.

Conclusion

This article has journeyed through the specifics of migrating from Mocha to Vitest using Codemod.com's platform, and here are the key takeaways:

  1. Codemods are powerful code modification bots that can help us modernize our stack at any scale, and keep up with the pace of new technologies.
  2. LLM: Thanks to cutting edge LLMs, any developer can instantly create codemods, saving hours, days and weeks of time in bigger codebases.
  3. OSS: With Codemod.com's open-source platform we can automate crucial yet tedious tasks for ourselves, colleagues, and the community.

If you, just like us, are passioante about the future of software development, join our community or team.

Start migrating in seconds

Save days of manual work by running automation recipes to automate framework upgrades, right from your CLI, IDE or web app.

Get started