Japa Lean Node.js Test Runner
Japa is a lean and fast Node.js test runner designed for both testing applications and for building custom test runners. The current stable version is v10.4.0, with minor and patch releases occurring frequently—typically several times a quarter—and major versions released periodically as breaking changes are introduced. A key differentiator is its minimal core, offering faster boot times compared to alternatives like Mocha or Ava, primarily because it does not ship with its own CLI; tests are executed directly as standard Node.js scripts. Japa supports ES6 async/await syntax, ES modules, test groups with lifecycle hooks, regression tests, and offers an extensible assertion system often utilized via plugins. Its design philosophy emphasizes simplicity and provides the foundational components for highly customized testing environments.
Common errors
-
TypeError: test is not a function
cause The main `test` function was imported incorrectly, often by attempting a named import when it is the default export, or by using CommonJS `require` syntax incorrectly.fixFor ESM, ensure you use `import test from 'japa'`. For CommonJS, use `const test = require('japa')`. -
TypeError: Cannot read properties of undefined (reading 'group')
cause The `test` function was not imported correctly, leading to `test.group` being called on an undefined or improperly imported `test` object.fixVerify that `test` is correctly imported as the primary export of the `japa` package. For ESM: `import test from 'japa'`. For CJS: `const test = require('japa')`. -
Error: Timeout of 5000ms exceeded for test "should perform a long running operation"
cause A test or a test group exceeded its configured timeout duration before completing execution, often due to long-running asynchronous operations or infinite loops.fixOptimize the test's execution time, increase the timeout duration using `test.timeout(ms)` or `group.timeout(ms)`, and ensure all asynchronous operations within the test are properly awaited.
Warnings
- breaking In v10.0.0, the way regression tests are reported by the emitter changed. This may break custom test reporters that rely on the `test:start` and `test:end` emitter events to determine the state of a test, especially for regression test scenarios.
- breaking With v9.0.0, the `title` property within `test:start` and `test:end` events no longer provides a custom `toString` method. Direct access to the test title should now use `event.title.original` or `event.title.expanded` properties.
- gotcha Japa is intentionally minimal and does not ship with a command-line interface (CLI). Tests are executed directly as standard Node.js scripts (e.g., `node test/my-test.spec.js`), which deviates from many other test runners that provide `test` or `run` commands.
- gotcha The `bail` mode, introduced in v10.0.0, causes the test runner to exit immediately upon the first failing test. While useful for CI/CD, this can halt entire test suites prematurely. Subsequent versions (v10.1.1, v10.2.0, v10.3.0) included fixes for `bail` mode idempotency and skipping behavior.
- gotcha While Japa includes 'inbuilt assertion library' as a feature, common practice and examples often show the use of `@japa/assert` as a plugin for a more robust and explicit assertion experience. Users expecting a globally available `assert` might be confused.
Install
-
npm install japa -
yarn add japa -
pnpm add japa
Imports
- test
import { test } from 'japa'import test from 'japa'
- Test
import Test from 'japa'
import { Test } from 'japa' - Runner
import Runner from 'japa'
import { Runner } from 'japa'
Quickstart
import test, { Runner } from 'japa'
import { assert } from '@japa/assert' // Japa typically uses plugins for assertions
// Configure Japa to discover test files and use the assertion plugin
test.configure({
files: ['**/*.spec.ts'], // Adjust glob pattern to your test file naming
plugins: [assert()]
})
// A simple function to be tested
function add(a: number, b: number): number {
return a + b
}
// Basic test demonstrating the main `test` function
test('should correctly add two positive numbers', ({ assert }) => {
assert.equal(add(2, 3), 5)
assert.notEqual(add(1, 1), 3)
})
// Test group with lifecycle hooks
test.group('Complex Operations', (group) => {
let result: number // State for the group
group.beforeEach(() => {
// Executed before each test in this group
result = 0
console.log(' [Group Hook] Resetting result to 0')
})
group.afterEach(() => {
// Executed after each test in this group
console.log(` [Group Hook] Current result after test: ${result}`)
})
test('should initialize result to 0', ({ assert }) => {
assert.equal(result, 0)
})
test('should allow adding values incrementally', ({ assert }) => {
result = add(result, 10)
assert.equal(result, 10)
result = add(result, 5)
assert.equal(result, 15)
})
}).timeout(5000) // Example of a group-level timeout in milliseconds
// Manually run the tests. This is optional if using a global runner setup.
const runner = new Runner()
runner.run()
.then(() => console.log('Tests completed.'))
.catch((error) => console.error('Tests failed:', error))