What's New on Node.js Test Runner and Why it's Game-changing

This ad is not shown to multipass and full ticket holders
React Summit US
React Summit US 2025
November 18 - 21, 2025
New York, US & Online
The biggest React conference in the US
Learn More
In partnership with Focus Reactive
Upcoming event
React Summit US 2025
React Summit US 2025
November 18 - 21, 2025. New York, US & Online
Learn more
Bookmark
Rate this content

Node's new test runner is pretty cool but not many people are using it yet. In my talk, I'll show you all the neat stuff it can do, including some features I worked on. We'll take a look under the hood of Node to see how mocks work and how to use them. I'll also chat about what's next for the runner and what to expect down the line. Get ready to up your testing game with native assertions and keep things running fast!

This talk has been presented at Node Congress 2024, check out the latest edition of this JavaScript Conference.

FAQ

The speaker of the talk is Luca Santos, a senior software engineer at OpenVault.

The main goal of the talk is to encourage people to switch from Jest to the Node.js Test Runner.

The speaker advocates for moving away from Jest because it is difficult to configure, especially for TypeScript, and it is slow due to its extensive features.

Current test runners are too difficult to configure, especially for TypeScript. They also add extra libraries on top of your project, are often not interoperable with each other, and can be either too extensible or not extensible at all.

The Node.js Test Runner is a built-in test runner in Node.js that is fast, requires no configuration, and integrates seamlessly with Node.js. It was added in April 2022 and became stable four months later in Node 20.

The benefits of using the Node.js Test Runner include no additional libraries to install, fast performance, no need for extensive configuration, seamless integration with Node.js, support for native assertions, and compatibility with other assertion libraries and test reporters.

The Node.js Test Runner supports features such as mocks, timers, date mocking, code coverage, test skipping, to-do tests, lifecycle hooks, and experimental watch mode.

You can contribute to the Node.js Test Runner by improving documentation, contributing code, providing feedback, and helping the community grow by shifting projects to it.

You can reach Luca Santos on his social networks by using the handle [social_network].elsantos.dev, or visit his main page at .elsantos.dev.

The Node.js Test Runner supports TypeScript out of the box by using importers to compile TypeScript on the fly. You can run TypeScript tests without needing extensive configuration.

Lucas Santos
Lucas Santos
17 min
04 Apr, 2024

Comments

Sign in or register to post your comment.
Video Summary and Transcription
The Node.js Test Runner is presented as a better alternative to Jest, offering more flexibility and improved performance. It supports TypeScript out of the box and provides comprehensive test suite visualization. The test runner has native support for code coverage and upcoming features include module mocking and improved filtering. Shifting to the test runner is simple and helps the community grow.

1. Introduction to Node.js Test Runner

Short description:

Hello, everybody. Today, we're going to talk about the Node.js Test Runner. I'm Luca Santos, a senior software engineer at OpenVault. You can reach out to me on Twitter and Instagram at elsantos.dev.

Hello, everybody. Today, we're going to talk about the Node.js Test Runner. So first of all, I'm going to introduce myself. My name is Luca Santos. I am one of the senior software engineers there at OpenVault today. I live in Sweden. I'm originally from Brazil. And you can reach out to me on any of my social networks. So basically, there's the ones in the bottom there. It's Twitter.elsantos.dev, Instagram.elsantos.dev. You know, you get the idea. Luca is a very common name in Brazil. So basically, I had to, you know, choose DNS over Nix in social networks. So just put the social network name followed by .elsantos.dev. Or if you are in doubt on any social networks that you want to talk to me, just .elsantos.dev is going to take you to my main page where everything is. Okay? I'm super open to talk about anything. So just reach out to me, and it's going to be super nice.

Read also

2. The Benefits of the Node.js Test Runner

Short description:

My goal with this talk is to make you ditch Jest for something better, the Node.js test runner. Jest is outdated and has limitations. The Node.js test runner is actively improved and offers more flexibility. The current test runners are difficult to configure, especially for TypeScript, and often require additional libraries that can slow down your project. They also lack interoperability and can be inflexible.

Next up, I'm going to share my goal in this talk for you. Like, what is the goal that I have with this talk? It's to make you ditch Jest in the end. That is simple as that. I don't hate Jest. I actually use it a lot. But I had my fair share of problems. And I think it's time to move on, right? I think it's time that we need something new, something better. Jest is super old, right? It's very good, but the time has passed, right?

So I'm going to do this. I'm going to present you something that is possibly better. But why possibly better? Well, because the Node.js test runner is still being actively improved. So there's a bunch of things that are happening. There are a bunch of things that we already have, but there's still a bunch of things missing. So you can help there if you want, okay?

But first, what is bad about the current environment? Why do we need another test runner? Isn't today enough? Isn't what we have today enough? So basically, the first thing is that they're too difficult to configure, especially for TypeScript. And they do a lot of things. Like configuring Jest is a pain. It's a super big configuration file. They do a lot of stuff. They do transforming, they do parsing, they do moving, they assert, they have coverage. It's just too many stuff. And this makes them slow, right? Because, well, it's a lot of stuff. And then you have a concept that they call yall, which is basically yet another library on top of your project. Because runners, in my opinion, should be a thing from your runtime. You shouldn't be able to install, you shouldn't install another library to have a runner. And remember, every other library, every package that install on top of your project is a code that you don't maintain, but yet you rely on it, right? So if something happens, you're gonna pay the price. And test runners, they seem to not want to interoperate with each other. They make sure that you will only be able to use them and nothing else. So it's very difficult to change from one to the other. They're completely different stuff. Most of them are pretty opinionated. So they are either super extensible or not extensible at all.

3. Advantages of the Node.js Test Runner

Short description:

There is no middle ground or extensibility with existing test runners, especially for TypeScript. They either lack flexibility or require additional libraries. The Node.js test runner stands out as a built-in solution that is fast, easy to use, and works seamlessly with Node.js. It supports native assertions and can be integrated with other test processors. It provides consistent results across your project.

There is no middle ground or something that you can extend, or if you don't want to extend. So basically, it's difficult to, you know, do what you want unless you have something super extensible but yet super difficult to use like Jest or something that is not extensible at all. If you want TypeScript, like TypeScript is the nemesis of all test runners because they mess it up completely. And if you want TypeScript, you can either do like a TypeScript native test runner, which there are not many today, or you have another library on top of a project that you need to install to be able to run this, right, completely.

And there's so many of them, test runners in Spawnlight, RATS, there's a lot of them. And it's basically a huge bloat because they don't add a lot of things to the ecosystem. They don't interoperate with each other. They basically, they're just the same thing with minor improvements from one another. But then enters the Node.js test runner. The Node.js test runner is not that new. It was added by Colin in 2022, April 8, 2022. And then this was the initial version, like the CLI. And then one of my former colleagues in Microsoft, he added a TAP parser. TAP stands for test anything protocol. We're going to talk about it a bit later. But it allowed you to interoperate the test runner with other test runners, other actually test processors. Then it was soon stable. About four months later, it was made stable in Node 20. So you can actually use it in Node 20 and it's stable for production. It can use normally and freely.

But why is it different from the other ones and why am I advocating for it so much? First of all, it's a built-in, no YL, right? So not another library on top of a project. It is pretty fast, very fast, to be honest. And you don't need to configure anything. It's actually, there is no rituals that you need to do to configure it. It works seamlessly in Node.js. You can use native assertions if you want, but I reckon that the native assertion library is not the amazing library as others, but you can actually change the libraries to any other assertion library. It outputs the TAP protocol, so it's easy to integrate. It accepts other test reporters as well. You can use always the most up-to-date features of Node.js because it's always up-to-date with Node.js features. The best thing is that it has consistency across your project.

4. Running Tests with the Node.js Test Runner

Short description:

The Node.js test runner is built-in, easy to use, and works seamlessly with Node.js. It supports TypeScript out of the box and can be extended with other assertion libraries. Running tests is straightforward, and the output includes comprehensive information about the test suites, canceled tests, and skipped tests.

So you have thousands of projects, everyone's gonna be using the same test runner and the same Node.js test runner that is building the runtime, right? So you're gonna rely on the runtime itself. There is no polyfills that you need to do, like babble things and blah, blah, blah. And it has experimental support for code coverage and for now, it's becoming stable over the years and other reporters as well. And it supports kinda TS out of the box. It's very easy to make it run as you would run TypeScript in Node.js normally, right?

Let me show you how it works. First, we declare the function that we wanna test and then we create our test, right? So let's break that thing down a bit. So first we write the test. So we just import our libraries, we import the describe and it. You can use just like with a test directly. I prefer to use like this because I think it's pretty much easier to read. Then you import assertion function, the assertion library. It's the same that we used in other libraries. It's just a bit different, it's not that easy to read but I like to use the native one because it's native. And then we import our function that we wanna test and then the test is basically the same test that we would write on any other test runner. So describe the name of the test suite and then it and then the test that you wanna run and then you have assert and that's it basically. If you don't want to use the scribe or any other thing you can actually import it directly as test from no test. And then that's what I told you, it's a bit more verbose but works just the same. And the assertion libraries, they are taken care of by the assert library module, which is built in but I reckon this is really not the best one. I probably like to say that you should use it because it's native, but it's not the best library built in over there. So you can actually use anyone you want like Chai or other thing that you want to use. So basically just import it, the only thing you may have to make sure is that the assertion library throws instead when there's an error. When there's an error it throws and then boom, that's fine. And like I did here, I just imported Chai from, expect from Chai and then basically works normally. The important thing is that the library, the assertion library is completely untied to the runner. So you can actually run whatever you want, it's extensible enough to do so. And then when you run, it's basically running node dash dash test and the name of your test file. Node just append the test command on the node command and then run your test files. It's gonna give you a tap output. First, you can see everything, like you can see all your test suits and then you can see the cancel test, you can see the skip test, it's also a feature that is supported.

5. Mocking and Controlling Test Behavior

Short description:

Node.js test runner supports comprehensive test suite visualization, including cancel, skip, and to-do tests. It also provides powerful mocking capabilities for functions, getters, setters, and methods. Additionally, it offers built-in support for mocking timers and dates, allowing you to easily control and manipulate time-related functionality in your tests.

First, you can see everything, like you can see all your test suits and then you can see the cancel test, you can see the skip test, it's also a feature that is supported. You can see the to-do test that's also a supported feature.

And then we have mocks. Mocks is one of the best and most important features of a test, actually. For me, it's the best feature of Jest is the mocks. So, but node also has them. It's, node supports mocking some of the built-in structures. So like, mock Fn is just like Jest Fn, like mocks and functions, so just spy function. It also mocks getters, setters, and can actually mocks methods for now, right.

But there are also two other complex structures that I'm gonna take you through. The first of those structures is the date, actually, actually the timers. This was added by a very good friend of mine, Eric. And basically, it mocks things like setTimeout, setInterval, and stuff like this. So basically, what you need to do is just create your test, and then we create a function, which is a spy function, and then we enable the mock timers functionality, saying which library, which API we want to mock. So basically, setTimeout's the one that we want to mock. And then we just set a timeout for like 99999 something. And then we are gonna assert that the timer's not called yet because the timer hasn't run. So we have to take that timer to run, and then we can assert again and say, hey, now this time is called. And we can reset the timer back to its original state.

You can also import the mocked timers directly from the node test directory. You don't need to import it as in the context like we're using here, like context.mocks. You can just import it directly from the test. It works as well, basically like we are doing here. And the next complex structure was added by yours truly, and it's time, it's dates. So this is a feature that I've personally worked on and building on previous structures like the setTimeout and setInterval, it allows you to mock the date object. So mocking dates in Node is, the only thing that we have to do different is that we have to enable the mocks to use the date API. And then we start the mock. It's gonna start with the Unix epoch, so January 1st, 1970. And then we can tick the mock, and then we can assert that the new date is gonna be that date plus 9999 milliseconds. So it's something that's super useful if you wanna deal with like messaging and stuff.

6. Code Coverage and TypeScript Support

Short description:

The Node.js test runner has native support for code coverage, using the V8 built-in coverage function. You can change coverage reporters and report to different formats or files. TypeScript is fully supported, allowing you to run TypeScript files without any additional configuration. The test runner also offers features such as abort signals, skipping tests, flagging specific tests, life cycle hooks, and name pattern filtering. The future of Node.js testing includes upcoming support for module mocking in the test runner.

So we use it a lot. And code coverage is the next step for the supports that I want to tell you about. So it has, the Node.js test runner has a native support. You just pass on the flag, which is experimental test coverage. It's gonna use the V8 built-in coverage function to give you an output like this. So it's gonna give you, this is the command that we are gonna run, and this is gonna be the tests that are gonna run. And then we have the usual TAP output that we have on the side, and then we have the start of the coverage report. As you can see, in the coverage result, we have three, two lines that were not covered, which are the ones that we throw here in our function. So it works as expected, right? And then you can also change the coverage reporters. You can use different reporters, and or reporting directly to files, using the test reporter or the test reporter destination. And right now the support, the native test runner supports these reporters. So you can use spec.tap, lcol, and JUnit reporters as well, natively, just put them in the test reporter, equals, and the name of the reporter.

But now let's talk about TypeScript, which is the thing that I love the most. So a bit of a side here, something on the side, but I don't know if you know all of this, but Node already supports you to compile TypeScript on the fly using importers, right? So importers were called loaders previously, and they are functions that execute before a module is loaded and they can also be used to transform a module before it is imported. So Babel has loaders, Jest has loaders, a lot of other tools that we use has loaders because you can import the module and then it's going to change the file that you're in, and then it's going to execute the file that you're in, right? So this means that you can also basically run TypeScript because you can just import a TypeScript file and then convert this to JavaScript, right? And then the common importers that we have today, there are ts-node, ts-sex, and ts-build. You can also build your own if you want, if you don't want to use any external libraries. I like ts-sex, I think it's one of the best ones out there. And for the TypeScript support, I've just changed the file that we had before. I just added some types, and then we can run it with import ts-sex and basically the ts file index.ts, and then boom, magic, right? But what if I told you that you can actually use this to run the test runner? So the TypeScript support, let's just take our previous file extension and we just append dash dash test to it. So boom, it runs, nothing else, like, it's magic. That's run, everything runs, there's no configuration, no nothing, we just run with native stuff, right? And when you look at this, like, 50 line configuration jazz file that you've been maintaining for this, you know, past five years, you're going to see that this is quite of a bliss because you don't have to maintain TypeScript anymore, so it's super, super nice. And there are other supported features for a node test runner so you can actually abort tests via abort signals. You can skip the test, like I said, the it skip or it to do to test that is still to be done. You can flag one test that you want to run, so with it only, it's funny, super nice. And we also have life cycle hooks like Jess does with before, after, before each, after each and stuff like this. And you can also filter the name pattern via rejects. Now you can actually do, I think Node 21 has added the support for globs, so you can actually add globs in the file names and you have a watch mode, which is experimental. But the future of Node.js testing, and funny enough, Colin himself has a list of what he wants for the Node.js test runner. So the first thing I wanted to show you is the module mocking. So basically the module mocking is something that is coming up in the test runner as well.

7. Upcoming Features and Conclusion

Short description:

The upcoming features in the test runner include module mocking, improved filtering with globs, snapshot testing, source map support, and more mocks. You can get involved by contributing or providing feedback. Shifting to the test runner is simple and helps the community grow. Connect with me on social media for more information.

It's a bit different from what I would expect, but the idea that you can actually mock the whole module and not just mock the methods or files is super cool. We also have improved filtering, allowing you to run tests based on test name patterns with globs. Snapshot testing, which was briefly added in the past but removed due to storage issues, is something that we want to bring back. Source map support is self-explanatory, and we aim to create more mocks for different functionalities, such as performance mocks. While these features are still in development, you can get involved by contributing to the documentation or code, or simply using the test runner and providing feedback. Shifting your projects to the test runner is simple, and it helps the community grow.

Thank you so much for your time today. If you want to connect with me, you can find me on my social networks. To access the QR code or link for this talk, please refer to the information I'm leaving here. I hope you enjoyed the talk and see you soon.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

How Bun Makes Building React Apps Simpler & Faster
React Day Berlin 2022React Day Berlin 2022
9 min
How Bun Makes Building React Apps Simpler & Faster
BUN is a modern all-in-one JavaScript runtime environment that achieves new levels of performance. It includes BUN dev, a fast front-end dev server, BUN install, a speedy package manager, and BUN run, a fast package runner. BUN supports JSX, has optimized React server-side rendering, and offers hot module reloading on the server. The priorities for BUN include stability, node compatibility, documentation improvement, missing features in BUN install, AST plugin API, native Windows support, Bundler and Minifier optimization, and easier deployment to production. BUN's AST plugin API allows for bundle-time JavaScript execution and embedding code, potentially inspiring new frameworks.
Node.js Compatibility in Deno
Node Congress 2022Node Congress 2022
34 min
Node.js Compatibility in Deno
Deno aims to provide Node.js compatibility to make migration smoother and easier. While Deno can run apps and libraries offered for Node.js, not all are supported yet. There are trade-offs to consider, such as incompatible APIs and a less ideal developer experience. Deno is working on improving compatibility and the transition process. Efforts include porting Node.js modules, exploring a superset approach, and transparent package installation from npm.
Bun, Deno, Node.js? Recreating a JavaScript runtime from Scratch - Understand magic behind Node.js
Node Congress 2023Node Congress 2023
29 min
Bun, Deno, Node.js? Recreating a JavaScript runtime from Scratch - Understand magic behind Node.js
The Talk explores the magic behind Node.js and delves into its components, including V8, libuv, and the C++ bridge. It discusses the workflow and execution process, the use of NodeMod, and the understanding of console functions. The Talk also covers Node.js functions and scheduling, the introduction of runtimes, and the collaboration between JavaScript runtimes. It concludes with insights on content production, the choice of Node.js, and the inspiration behind it.
Eval all the strings! - Hardened JavaScript
Node Congress 2023Node Congress 2023
8 min
Eval all the strings! - Hardened JavaScript
NPM packages can be potentially dangerous, so it's important to be proactive in managing them. Lava Mode allows you to detect and investigate suspicious packages before deploying your app. Lavamote prevents unauthorized access to sensitive resources by isolating dependencies and using hardened JavaScript. Lava Mode makes it easier to analyze obfuscated files and understand their actions.
The Future of JavaScript Runtimes
Node Congress 2022Node Congress 2022
34 min
The Future of JavaScript Runtimes
Today's Talk explores the future of JavaScript runtimes, their evolution, and impact on software development. It discusses the historical trends of JavaScript, the adoption of new tools and libraries, and the convergence of Node and Deno. The emergence of isolate clouds and their potential to replace traditional VMs and containers is also highlighted. Additionally, the talk touches on the possibilities of JavaScript in exotic use cases, its impact on machine learning, and the potential for TypeScript to become the de facto language for JavaScript development.
Roll you own JavaScript runtime
Node Congress 2023Node Congress 2023
21 min
Roll you own JavaScript runtime
This Talk introduces Deno, a custom JavaScript runtime similar to Node.js, and discusses the benefits of using a custom runtime. It explores the process of building a custom runtime with Deno in Rust, including the integration with Cargo and the ability to mix and match crates and APIs. The Talk also covers the implementation of the setTimeout function in both Run.js and Runtime.js, and provides examples of how to fix errors and implement additional functions. Overall, the Talk highlights the flexibility and possibilities of creating custom runtimes with Deno.

Workshops on related topic

Build Peer-to-Peer Applications with Pear Runtime
JSNation 2024JSNation 2024
152 min
Build Peer-to-Peer Applications with Pear Runtime
WorkshopFree
David Mark Clements
David Mark Clements
Learn how to rapidly build peer-to-peer applications with Pear Runtime. No servers required. Understand peer-to-peer paradigms and construct applications from well-defined building blocks. This workshop will cover how to create both Desktop and Terminal applications (with discussion for Mobile) that work entirely peer-to-peer from anywhere in the world. By the end of this workshop you should know how to build a new type of highly scalable application with entirely reduced infrastructural costs (~0) along with suitable architectures and best practices for peer-to-peer applications. From the creator of Pear Runtime and the company that brings us keet.io. Table of content:- Introducing Pear- Initial Q & A- Getting Setup- Creating a Pear Desktop Application- Sharing a Pear Application- Running a Pear Application- Creating a Pear Terminal Application- Releasing a Pear Application- Architectural Discussions- Wrap-up Q & A