惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Secure Thoughts
雷峰网
雷峰网
罗磊的独立博客
T
The Blog of Author Tim Ferriss
阮一峰的网络日志
阮一峰的网络日志
量子位
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
云风的 BLOG
云风的 BLOG
人人都是产品经理
人人都是产品经理
GbyAI
GbyAI
Cisco Talos Blog
Cisco Talos Blog
Engineering at Meta
Engineering at Meta
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
A
About on SuperTechFans
D
Darknet – Hacking Tools, Hacker News & Cyber Security
The Cloudflare Blog
Know Your Adversary
Know Your Adversary
T
Threat Research - Cisco Blogs
Spread Privacy
Spread Privacy
D
DataBreaches.Net
T
The Exploit Database - CXSecurity.com
K
Kaspersky official blog
Cyberwarzone
Cyberwarzone
爱范儿
爱范儿
U
Unit 42
Security Latest
Security Latest
M
MIT News - Artificial intelligence
月光博客
月光博客
Scott Helme
Scott Helme
G
Google Developers Blog
有赞技术团队
有赞技术团队
T
Tor Project blog
宝玉的分享
宝玉的分享
Y
Y Combinator Blog
博客园 - Franky
H
Hackread – Cybersecurity News, Data Breaches, AI and More
aimingoo的专栏
aimingoo的专栏
The GitHub Blog
The GitHub Blog
V
V2EX
B
Blog
Apple Machine Learning Research
Apple Machine Learning Research
S
Securelist
博客园 - 三生石上(FineUI控件)
Blog — PlanetScale
Blog — PlanetScale
TaoSecurity Blog
TaoSecurity Blog
Stack Overflow Blog
Stack Overflow Blog
P
Proofpoint News Feed
腾讯CDC
D
Docker
Google Online Security Blog
Google Online Security Blog

The Astro Blog

Astro 6.4 Astro 6.3 Starlight 0.39 Astro 6.2 What's new in Astro - April 2026 What's new in Astro - March 2026 Astro 6.1 CloudCannon Joins Astro as an Official CMS Partner Astro 6.0 What's new in Astro - February 2026 What's new in Astro - January 2026 Astro 5.17 Supporting the future of Astro The Astro Technology Company joins Cloudflare Astro 6 Beta What's new in Astro - December 2025 What's new in Astro - November 2025 Astro 5.16 Stainless Sponsors Astro, Launches Astro-Powered Docs Platform What's new in Astro - October 2025 Astro 5.15 Spirit of Astro: meet the winning designs What's new in Astro - September 2025 Astro 5.14 Cloudflare Donates $150,000 to Support Astro's Open Source Mission Webflow Donates $150,000 to Support Astro's Open Source Mission Mux: Our Official Video Partner Unleashing creativity: How CodeTV built a video streaming platform with Astro and Mux | Astro What's new in Astro - August 2025 Astro 5.13 What's new in Astro - July 2025 Astro 5.12 Starlight 0.35 Astro 5.11 What's new in Astro - June 2025 Live Content Collections: A Deep Dive Introducing… Astro Mart Astro Solstice Festival Astro 5.10 Astro 5.9 What's new in Astro - May 2025 Astro 5.8 What's new in Astro - April 2025 2025 Technical Steering Committee Starlight April Update Astro 5.7 Astro Agency Partner Program Astro 5.6 What's new in Astro - March 2025 Astro 5.5 What's new in Astro - February 2025 Astro 5.4 Starlight 0.32 Astro 5.3 What's new in Astro - January 2025 Astro 5.2 2024 year in review What's new in Astro - December 2024 Astro 5.1 Astro 5.0 Google IDX: Our Official Online Editor Partner What's new in Astro - November 2024 What's new in Astro - October 2024 Astro x Cloudinary SDK What's new in Astro - September 2024 Community Loaders for Astro Content Layer Astro x Hygraph: Content Loader Astro x Cloudinary: Content Loader Astro x Storyblok: Content Loader Content Layer: A Deep Dive Starlight 0.28 Astro 5.0 Beta Release The $100,000 Astro Ecosystem Fund Fall Update Goodbye Studio, Hello DB What's new in Astro - August 2024 Astro 4.15 Astro 4.14 Astro 4.13 What's new in Astro - July 2024 Astro 4.12: Server Islands Netlify: Our Official Deployment Partner What's new in Astro - June 2024 Astro 4.11 Astro Together 2024 Server Islands The Astro Content Layer Zero-JavaScript View Transitions Astro 4.10 Starlight turns one year old! What's new in Astro - May 2024 Astro 4.9 Astro 4.8 What's new in Astro - April 2024 Astro 4.7 Astro 4.6 What's new in Astro - March 2024 Astro DB: A Deep Dive The Astro Developer Portal Astro DB Astro 4.5
Migrating 500+ tests from Mocha to Node.js
Emanuele Stoppa, Bjorn Lu · 2024-03-25 · via The Astro Blog

Over a month ago, we discussed a possible migration to the Node.js test runner. While we were sufficiently happy with Mocha, we are always looking to make our CI jobs faster.

Relying on a test runner baked inside our runtime had some advantages for our main monorepo:

  • Two fewer dependencies to install and maintain in our monorepo: mocha and chai.
  • Maintainability: there are more people involved in the Node.js project to maintain the Node.js test runner.
  • Future benefits: we believe the test runner will improve with time, and eventually save some time in our CI workflows.

From an idea to a PoC

The Astro monorepo has more than 500 testing suites: between integration tests and unit tests, we have 664 suites, with a total of 1603 tests. The majority of these tests are integration tests.

An integration test, in our monorepo, means creating a tiny Astro project, building this project with a specific environment (development, static generation (SSG) or dynamic generation (SSR)), and then running assertions over the built pages. That’s right, each integration test requires vite to build and bundle the project.

Before deciding to undertake this migration, we wanted to make sure that moving away from Mocha was not a mistake. Despite its quirks, Mocha is a very capable test runner! It’s been around for a long time and is battle-tested. If you use Mocha, you are in good hands.

The idea of the PoC was to understand:

  • The flexibility of the Node.js CLI arguments and how customizable the test reporters could be.
  • The speed of execution of the testing suites.
  • The overall developer experience.

How we started

We started by migrating only one of our packages that didn’t already use astro’s integration suite: create-astro. This was a good opportunity to play with the built-in assertion library node:assert, to learn about the options it offered, and evaluate its performance compared to Mocha.

Since create-astro only had a handful of tests, it was relatively easy to migrate the test files to use node:test and node:assert instead of mocha and chai. After that, the only thing left was to update the mocha command to node --test to execute the tests. However, we quickly ran into issues using the node --test command, including:

  • It had trouble parsing the glob syntax when passing multiple arguments (e.g. node --test "test/*.test.js" --test-name-pattern="foo").
  • It wasn’t possible to pass the --test-concurrency flag (only available in Node.js 21 and above), but could be worked around by using the programmatic API concurrency option.
  • Nitpicking, but the argument names were verbose: --test-name-pattern instead of --match, -m arguments; --test-timeout instead of --timeout, -t arguments, etc.

Hence, to solve these issues, we created a custom script which can be called with the astro-scripts test command. This decision also proved useful in order to enable more workarounds as you’ll see later.

Opening Pandora’s box

After successfully migrating the first package, we then attempted to migrate the testing suites of the @astrojs/node package. This integration is one of our most downloaded integrations, so we have plenty of tests. Plus, the tests of this package all have integration tests, so it was a good opportunity to check the performance of the test runner.

Once the PR was ready, we noticed that Node.js test runner was significantly slower than Mocha. We investigated, and we discovered that Node.js spawns a new process for each test file to ensure that each testing suite is run in isolation. Running a testing suite in isolation is, generally, a good practice because it assures that tests run in an unpolluted environment.

However, our testing suites were already isolated. In fact, we were able to run our testing suites with Mocha using the main thread without running into any of the typical issues: side effects, polluted environments, etc. Unfortunately, Node.js didn’t provide an option to run all tests in the same thread. So, we had to come up with a solution to the slow test runs. (Aren’t we engineers, after all? We solve problems!)

Using our internal astro-scripts test command, we were able to work around this by creating a temporary file that imports all the testing suites, and we let Node.js test that single file. This way, only one process is spawned for the file and we reach the same level of performance as if we were using the main process.

However, this came with its own downside: if there was a test failure or a timeout, we weren’t able to tell which test was the cause. This was the main quirk we found, and while not every team may have made this choice, we accepted this trade-off in order to give us the benefits mentioned earlier. After all, we had previously accepted Mocha’s quirks!

Node.js assert and chai

During the migration, we had to remove the chai library for node:assert/strict. This task uncovered that with chai, you can execute the same check in different ways. For example, you can run an equality check at least in four different ways:

import { expect } from "chai";

expect("foo").to.eq("foo")

expect("foo").to.be.eq("foo")

expect("foo").to.equal("foo")

expect("foo").to.be.equal("foo")

On the one hand, it’s good to have this kind of flexibility. But on the other, the code for the tests becomes inconsistent. With the Node.js assertion module, there is only one way to perform this check:

import { assert } from "node:assert/strict";

assert.equal("foo", "foo")

The Node.js assertion module provides almost all of the functionalities we required, so the migration from chai wasn’t as painful as we thought it might be. Our use of chai was very minimal. However, we do miss the .includes shortcut of chai:

import { expect } from "chai";

expect("It's a fine day").includes("fine")

The Node.js assertion module doesn’t provide such a utility, so instead we ended up using the equality assertion with the String#includes function:

import assert from "node:assert/strict";

assert.equal("It's a fine day".includes("fine"), true)

Here come the dragons

As mentioned before, we have a lot of test files and we add new tests almost every day. Opening a one-off PR that does the migration of the whole monorepo is unfeasible. It would require a lot of work from one person, and keeping the branch updated would be stressful.

So we came up with a simple plan:

  • Migrate first the small packages inside the monorepo.
  • Slowly migrate the main package - astro - by having Mocha and Node.js test runner in the same CI.
  • Remove Mocha.

In order to achieve that, we asked for help from our community. We thought this was the perfect opportunity to let people that aren’t familiar with Astro’s business logic contribute to the project. And, we could make the migration process go more quickly.

We created and pinned an umbrella issue to coordinate the efforts. Each contributor took ownership of the migration of an individual package, opening a separate PR for each one. Two new first-time contributors to the project even joined the efforts. It was a fantastic thing to see. In one week, we were able to migrate all packages!

Migrating the main package astro was a feat! It’s the package that contains by far the highest number of tests. In order to perform this migration slowly and carefully, we had to come up with an out-of-the-box solution.

We set up the Node.js test runner to test only the files called *.nodetest.js. Doing so allowed us to keep testing all files in the CI. Then, the rest was just a matter of coordinating our community by providing (and documenting!) a clear process for them to follow:

  • Use the umbrella issue to tell other contributors which files you intend to migrate;
  • Rename the files to migrate from *.test.js to *.nodetest.js;
  • Migrate the files;
  • Open a PR, wait for a review, and if successful, an Astro maintainer will merge the PR.

With the help of @log101, @mingjunlu, @VoxelMC, @alexnguyennz, @xMohamd, @shoaibkh4n, @marwan-mohamed12, @at-the-vr and @ktym4a we migrated almost 300 test suites in one week!

The results

We are quite happy with the results. We haven’t seen any significant regression in the performance of our tests. The assertion module that Node.js provides has all the utilities we need, and the describe/it pattern is supported, so the migration from Mocha was smooth.

There are, however, still a few hiccups regarding the developer experience compared to using Mocha.

For example, to run one single test suite in Mocha, using it.only is enough. With the Node.js test runner you have to:

  • Run the CLI using the --test-only argument.
  • Add .only to the describe that contains the it.only you want to run.
  • If there are multiple instances of describe, all of them need to be marked with .only.

Another example is that using --test-name-patterns could be improved. This argument is used to run only the tests that match a particular name pattern. The DX isn’t great because the CLI litters the terminal with messages about tests that don’t match (which aren’t run). This makes it more difficult to understand which tests are actually run. Plus, the command is really slow just for running tests that match some pattern.

Node.js test runner is still young and with its active development, has all the right cards to become better. For example, the Node.js project is currently evaluating running tests using the main process after we voiced our use case.

In the spirit of true open-source collaboration, we are pleased that improving Astro by switching our tests to Node.js will, in turn, improve Node.js itself!