Node Tap is a fast and lightweight testing tool for Node.js. It’s built to work smoothly with modern JavaScript projects.
Node Tap gives you a clean, easy-to-use API, quick test runs, and TAP-friendly output. It works great for both front-end and back-end JavaScript apps.
This tutorial will explore Node Tap’s main features and learn how to write and run tests effectively.
Prerequisites
To follow this guide, make sure you have Node.js installed—preferably the latest LTS version, which is 22 at the time of writing. You should also be comfortable writing basic JavaScript programs.
Step 1 — Setting up the directory
In this step, you’ll create a new project folder, set it up with npm, and install Node Tap so you can start writing tests.
Start by creating a new directory and moving into it:
Then, initialize the directory as an npm project:
This command creates a package.json file, which stores metadata and dependencies for your project.
Next, install Node Tap as a development dependency:
Then, enable ES Modules in your project:
This command automatically adds "type": "module" to your package.json file without manually editing it, telling Node.js to treat all .js files as ES modules.
Now, create a .taprc file to help with ESM compatibility issues:
This configuration file helps avoid common issues when running Node Tap with ES Modules.
After that, add a test script using npm:
This command automatically adds the test script to your package.json file.
You're now ready to create and test a simple JavaScript function using Node Tap. Here's the function in full:
The add() function takes two numbers and returns their sum. Save this file at the root of your project.
Next, let's write the first test using Node Tap.
Step 2 — Writing your first test
Writing unit tests makes it easier to catch bugs early by checking if your functions return the right results in different situations. With Node Tap, you can automate these checks instead of testing everything manually.
Start by creating a test directory in your project root:
Next, create a file named math.test.js inside the test directory and add the following test:
The tap.test function organizes related tests within a test suite, providing a clear structure to the test file.
Within this suite, the test function defines individual test cases, each specifying a particular behavior to verify.
To ensure correctness, the t.equal function performs assertions, checking whether add(1, 2) returns 3 as expected.
Now that the test is written, let's run it in the next step.
Step 3 — Running your tests
Now that your test file is ready, it’s time to see it in action. Node Tap will automatically find and run any test files inside your test directory.
To execute all tests, run:
Node Tap automatically looks for test files inside the test folder and runs them for you. When you run your tests, you’ll see output in the terminal that looks something like this, with colors like green for passing tests:
The test runner successfully detected and executed the math.test.js file, confirming that the test suite ran as expected. Alongside the results, the execution time for each test case is displayed, providing insight into the performance of the test run.
Step 4 — Test filtering and running specific tests
Running all your tests every time can get slow as your project grows. Node Tap lets you run only the tests you need to stay efficient. With built-in filtering options, you can quickly focus on specific test files or even individual test cases—making development and debugging much faster.
Using .only to focus on specific tests
If you want to run just one test or a group of tests within a file, you can use the .only method. This will execute only the test(s) marked with .only, allowing you to focus on specific functionality during development.
For example, if you're working on a feature and only want to run the add function test, modify the test like this:
When you add .only to a test, Node Tap will run "only" that specific test and skip the rest. This is useful when you're debugging or focusing on a single feature.
Run the tests with the --only flag:
Only the test marked with .only will run—everything else will be ignored until you remove it:
As shown in the output above, the first test ran successfully, while the second was skipped because it wasn't marked with .only.
This allows you to zero in on the functionality you're working on without running the entire suite.
Using .skip to temporarily exclude tests
If you want to leave out certain tests temporarily—maybe they’re still in progress or causing problems—you can use .skip. This tells Node Tap to ignore the test during the run.
For example, to skip the first test, change it like this:
Run the tests:
The first test will be skipped, but the second will run:
As shown in the output above, the test with .skip is excluded from the run, allowing you to focus on the tests that are relevant to your current work.
Filtering tests with command line arguments
Node Tap gives you flexible command-line options to run only the tests you care about—no need to change your code. This is super handy in CI/CD pipelines or when you just want to focus on a specific part of your project.
For example, let’s say you only want to run tests related to the add function. Here’s what the full test suite looks like without any filters:
You can filter tests based on their description using the --grep flag. This allows you to run only tests that match a specific pattern in their description, which is perfect for isolating functionality while working on a particular feature or debugging an issue.
To run the tests that mention the additional in their description, use the following command:
The double dash -- separates npm's arguments from those passed to Node Tap. This command runs only tests that include the string "additional" in their descriptions. The output should look like this:
In this example, both tests related to the additional are run, and the output confirms that both tests passed.
Step 5 — Using in-source tests
Node Tap makes writing tests directly in your source code files easy, eliminating the need for separate test files. This approach, known as in-source testing, is perfect for quickly testing functions without creating and managing additional files.
For example, you can modify the math.js file that contains the add() function to include tests right alongside the code:
With this approach, the tests are written directly below the function. When you run the file with Node, Node Tap will automatically detect and run these tests:
The output will look something like this:
The output shows both tests ran successfully. The key here is the isMainModule check, which ensures tests only run when you execute the file directly—not when you import it into other files. This prevents your tests from running in production code.
While in-source testing is convenient for small or quick tests, separating tests from source code is generally better for larger projects. This separation improves maintainability and makes your codebase easier to navigate, especially when working in teams.
We'll stick with separate test files for the rest of this guide. Here's the original math.js function without the tests:
Now that you've learned how to embed tests directly in your source files, let's move on to more advanced techniques like mocking external dependencies.
Step 6 — Mocking with Node Tap
When testing code that interacts with external dependencies like the file system, databases, or APIs, you often need to simulate these dependencies rather than using them directly. Node Tap provides a built-in mocking system that makes this easy.
First, create a fileReader.js file in your project root with a function that reads file contents:
This function uses Node's native file system module to read a file and return its contents as a string.
Now, let's create a test file that uses Node Tap's mocking capabilities to test this function without accessing the real file system:
This test uses Node Tap's t.mockImport() method, which is specifically designed for mocking dependencies in ES modules. When the tested module imports node:fs/promises, it will receive our mock version instead of the real one.
Our approach replaces the entire fs/promises module with a mock object containing just the necessary functions. The first test verifies that our function correctly calls readFile with the right arguments and properly trims the returned content. The second test confirms that errors are caught and wrapped with helpful context.
Run the tests to see them in action:
Mocking has a few key benefits: it speeds up tests, removes reliance on real files, and gives you full control over what external dependencies return. This makes it easier to test edge cases and errors, while keeping your tests focused on your own code.
You can use this same pattern to mock APIs, databases, or any other external system—not just the file system.
Final thoughts
Node Tap is a powerful and lightweight tool that makes testing in Node.js straightforward and efficient. With support for ES modules, in-source testing, mocking, filtering, and built-in code coverage, it gives you everything you need to build reliable, well-tested JavaScript applications.
Whether you're writing simple unit tests or building out a full CI/CD pipeline, Node Tap helps you catch bugs early, speed up development, and keep your codebase solid