jeta
Jeta
Jeta is the assertion and testing package for Jetjs. It provides a lightweight testing toolkit with assertions, suites, function spies, and CommonJS module mocking.
Jeta is part of the Jetjs ecosystem, but it can also be installed and used independently in CommonJS Node.js projects.
Contents
- Quick start
- Multiple test files
- Using Jeta with Jetjs
- Assertions
- Suites and tests
- Function spies
- Module mocking
Installation
Install jeta as a development dependency:
npm install --save-dev jeta
Jeta uses CommonJS modules.
Quick start
Create a test file:
// calculator.test.js
const {describe, expect, result, test} = require('jeta');
function add(a, b) {
return a + b;
}
describe('Calculator', () => {
test('adds two numbers', () => {
expect(add(2, 3)).toBe(5);
});
test('returns a number', () => {
expect(add(2, 3)).toBe(expect.any(Number));
});
});
process.exit(result());
Add a test script to package.json:
{
"scripts": {
"test": "node calculator.test.js"
}
}
Run the tests:
npm test
Jeta prints each test result and exits with status 0 when all tests pass or status 1 when one or more tests fail.
Multiple test files
Tests can be colocated with the files they cover:
project/
├── src/
│ ├── calculator.js
│ ├── calculator.test.js
│ ├── formatter.js
│ └── formatter.test.js
├── test.js
└── package.json
Each test file defines its own suites and tests:
// src/calculator.test.js
const {describe, expect, test} = require('jeta');
const {add} = require('./calculator');
describe('Calculator', () => {
test('adds two numbers', () => {
expect(add(2, 3)).toBe(5);
});
});
Use a central runner to load every test file explicitly, then report the result:
// test.js
const {result} = require('jeta');
require('./src/calculator.test');
require('./src/formatter.test');
process.exit(result());
{
"scripts": {
"test": "node test.js"
}
}
Jeta does not automatically discover test files. Explicitly requiring them keeps the test setup predictable and dependency-free.
Using Jeta with Jetjs
When Jeta is used as part of the Jetjs framework, jetc can run a test entry point that discovers and evaluates the files in src.
If the project uses JSX and the jete package is available, Jeta also exposes
render for rendering UI components as HTML strings in tests:
import {describe, expect, render, test} from 'jeta';
import App from './app';
describe('App routes', () => {
test('Renders the home route', () => {
expect(render(<App />)).toInclude('Home');
});
});
render delegates to jete.renderToString. It is optional: projects that do not use JSX can use Jeta without installing jete, but calling render without jete installed will throw an error.
Create test.js:
import path from 'path';
import {discover, evaluate, result} from 'jeta';
export default () => {
discover(path.join(process.cwd(), 'src'))
.then(evaluate)
.then(() => process.exit(result()))
.catch((error) => console.error(error));
};
Add the test script to package.json:
{
"scripts": {
"test": "jetc test.js"
}
}
Run the tests:
npm test
Assertions
assert(condition)
Passes when the condition is truthy and fails when it is falsy.
const {assert} = require('jeta');
assert(2 + 2 === 4);
Use assert.not to assert the opposite:
assert.not(false);
assert.not(2 + 2 === 5);
expect(received).toBe(expected)
Compares the received and expected values. Plain objects and arrays are compared recursively.
expect(42).toBe(42);
expect({name: 'Jeta'}).toBe({name: 'Jeta'});
expect([1, {active: true}]).toBe([1, {active: true}]);
expect(NaN).toBe(NaN);
Negate an expectation with .not:
expect(42).not.toBe(10);
expect({active: true}).not.toBe({active: false});
expect(received).toInclude(expected)
Checks whether a string contains a substring or an array contains an equal value.
expect('Welcome to Jetjs').toInclude('Jetjs');
expect(['assert', 'expect', 'test']).toInclude('expect');
expect([{id: 1}, {id: 2}]).toInclude({id: 2});
Array values use the same recursive comparison as toBe:
expect([{name: 'Jeta'}]).toInclude({name: 'Jeta'});
String matching requires a string value and does not coerce other types:
expect('version 2').toInclude('2');
expect('version 2').not.toInclude(2);
Negation is supported:
expect('Jeta').not.toInclude('Java');
expect([1, 2, 3]).not.toInclude(4);
expect.any(type)
Matches any value with the expected JavaScript typeof result. Pass a constructor such as Boolean, Number, or String, or pass a type name such as 'function'. It can be used with toBe, inside nested values, and with array inclusion.
expect(true).toBe(expect.any(Boolean));
expect('Jeta').toBe(expect.any(String));
expect({id: 12}).toBe({id: expect.any(Number)});
expect([false, 10]).toInclude(expect.any(Boolean));
Call expect.any() without a type to match any value:
expect('anything').toBe(expect.any());
expect.re(pattern)
Creates a RegExp from the supplied pattern and tests the received value with it:
expect('Welcome to Jeta').toBe(expect.re('Jeta'));
expect('release-42').toBe(expect.re('release-\d+'));
It can also match values contained in an array:
expect(['alpha', 'release-42']).toInclude(expect.re('release-\d+'));
Suites and tests
describe(title, suite)
Groups related tests under a title:
describe('User', () => {
test('has a name', () => {
expect({name: 'John'}).toBe({name: 'John'});
});
});
test(description, condition)
Runs a synchronous test condition and records whether it passed or failed. An assertion failure or any other thrown error fails the test.
Jeta does not await promises. Complete asynchronous work before making assertions or finishing the test callback.
test('adds numbers', () => {
expect(1 + 2).toBe(3);
});
result()
Prints the total number of suites, passed tests, and failed tests. It returns:
0when no tests failed, including when no tests ran1when at least one test failed
Call it after all test files have loaded:
const {result} = require('jeta');
require('./src/calculator.test');
require('./src/formatter.test');
process.exit(result());
Function spies
fn(implementation)
Creates a spy function. An optional implementation controls its behavior:
const {expect, fn} = require('jeta');
const add = fn((a, b) => a + b);
expect(add(2, 3)).toBe(5);
add.hasBeenCalled(1);
add.hasBeenCalledWithArgs(2, 3);
Without an implementation, the spy returns undefined:
const callback = fn();
callback('complete');
callback.hasBeenCalled();
callback.hasBeenCalledWithArgs('complete');
hasBeenCalled() passes after one or more calls. Supply a number to require an exact call count:
const callback = fn();
callback();
callback();
callback.hasBeenCalled(2);
hasBeenCalledWithArgs() compares the arguments from the most recent call.
Module mocking
mock(path, exportName, implementation)
Replaces a named CommonJS export with a spy. It is intended for exported functions.
Create the mock before requiring the module and any modules that depend on it. References captured before mock() is called cannot be replaced.
const {expect, mock} = require('jeta');
const requestMock = mock('./request', 'get', () => ({status: 200}));
const request = require('./request');
expect(request.get('/users')).toBe({status: 200});
requestMock.hasBeenCalledWithArgs('/users');
requestMock.restore();
Always call .restore() when the test no longer needs the mock:
test('loads a user', () => {
const get = mock('./request', 'get', () => ({id: 1}));
try {
const {loadUser} = require('./user');
expect(loadUser()).toBe({id: 1});
get.hasBeenCalled();
} finally {
get.restore();
}
});
API
Jeta exports:
const {
assert,
describe,
discover,
evaluate,
expect,
fn,
mock,
render,
result,
test
} = require('jeta');
License
ISC