npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

hoare

v3.2.0

Published

A simple and opinionated Typescript/Javascript test runner designed to help you to write simple, readable, and maintainable tests.

Downloads

168

Readme


build status semantic-release Conventional Commits SemVer

An easy-to-use, fast, and defensive Typescript/Javascript test runner designed to help you to write simple, readable, and maintainable tests.

Named after Sir Tony Hoare, the inventor of the Hoare Triple which is the cornerstone of unit testing.

🔒 Out-of-the-box Typescript support

  • Written in Typescript. No special configuration needed, and no plugins to install.
  • Works great with c8 for code coverage.
  • Handles compilation errors gracefully.

🛡 Defensive

  • Uncaught errors and unhandled promise rejections will cause the test to fail.
  • Any spec files without tests, or tests without assertions, result in a failed test.
  • Strict and deep equality comparison by default.

🚀 Fast & Reliable

  • Multi-process parallel test runner. Each spec file is run in its own process and runtime for speed and isolation benefits.
  • Optimized for speed and simplicity.
  • Minimal dependencies.

😀 Easy to Use

  • Simple assertion API. You shouldn't need to learn a new language to read and write tests. Assertions should be simple axiomatic logic.
  • Built-in powerful diff visualization tool.
  • Errors or unhandled promise rejections are buffered and grouped under the test file in the output. This helps you know where they came from.

✨ Modern Features

  • Async/Await/Promise Support
  • Simple API facilitates functional programming patterns and AI test generation.

Table of Contents

Examples

Hello World

helloworld.ts

export function helloworld() {
  return 'hello, world';
}

helloworld.spec.ts

import {test} from 'hoare';
import {helloworld} from './helloworld';

test('should return "hello, world"', (assert) => {
  assert.equal(helloworld(), 'hello, world');
});

Mocking Dependencies

You can use cjs-mock to mock dependencies (only works with CommonJS modules). This is especially useful for mocking file system operations, network requests, or other side effects.

isValidWord.ts

import {readFile} from 'fs/promises';

export async function isValidWord(word: string) {
  const validWords = await getValidWords();
  return validWords.indexOf(word) !== -1;
}

async function getValidWords() {
  const contents = await readFile('./dict.txt', 'utf-8');
  return contents.split('\n');
}

isValidWord.spec.ts

import {test} from 'hoare';
import {mock} from 'cjs-mock';
import * as mod from './isValidWord'; // just used for typing

const dict = ['dog', 'cat', 'fish'].join('\n');
const mockMod: typeof mod = mock('./isValidWord', {
    'fs/promises': {readFile: () => Promise.resolve(dict)},
});

test('valid word returns true', async (assert) => {
  const result = await mockMod.isValidWord('dog');
  assert.equal(result, true);
});

test('invalid word returns false', async (assert) => {
  const result = await mockMod.isValidWord('nope');
  assert.equal(result, false);
});

For more examples, see examples or src.

Installation & Setup

Typescript w/ code coverage using c8

  1. Install from npm along with peer dependencies:

    npm i typescript ts-node c8 hoare -D
  2. Make sure your tsconfig.json file has the following compiler options set:

    {
      "module": "CommonJS",
      "sourceMap": true
    }

    Note: If you are using a different module systems such as ESM, you can create a separate tsconfig.test.json file and use the --project flag with tsc or ts-node, or use command line flags.

  3. Create an .c8rc.json file in the root of your project (or use another config option), following the c8 documentation. For an example, see our .c8rc.json file.

  4. Add the following command to your package.json scripts directive:

    {
      "test": "c8 hoare 'src/**/*.spec.@(ts|js)' && c8 report -r text -r html"
    }

The above command, along with our .c8rc.json file settings, will do the following:

  1. Run c8 for code coverage.
  2. Run any .spec.js or .spec.ts file within the src folder, recursively.
  3. If the test is successful, generate both an HTML and text coverage report.

You can customize the above command to your situation. The string in quotes is a glob.

Javascript w/ code coverage using c8

  1. Install from npm along with c8:

    npm i c8 hoare -D
  2. Add the following command to your package.json scripts directive:

    {
      "test": "c8 hoare 'src/**/*.spec.js' && c8 report -r text -r html"
    }

Javascript w/o code coverage

  1. Install from npm:

    npm i hoare -D
  2. Add the following command to your package.json scripts directive:

    {
      "test": "hoare 'src/**/*.spec.js'"
    }
    

Running hoare via npx

# glob
npx hoare 'src/**/*.spec.ts'

# a specific file
npx hoare 'src/foo.spec.ts'

Basic Usage

  1. Write your tests with a .spec.ts or .spec.js extension (although any extension will work as long as it matches the glob provided).

    Tip: we recommend you put your source code in a src folder and keep your test files alongside the source, and not in a separate folder.

  2. Simply run npm test, or directly via npx as shown above.

Example file structure:

dist // built files
src
  foo.ts
  foo.spec.ts

API

Methods

test(title: string, cb: (assert: Assert) => void): void

Create a test. cb can be an async function.

Assertions

equal

equal(actual: any, expected: any, msg?: string): void

Asserts deep and strict equality on objects or primitives. This will give you a visual diff output for any discrepancies.

throws

throws(experiment: () => any, expectedError: Error, msg?: string): void

Asserts that the function experiment throws an error that is equivalent to expectedError, ignoring stack traces.

It uses errorsEquivalent() under the hood, so it will check for both non-enumerable properties (ie, name and message) and enumerable properties (anything added by extending Error).

Example:

import {test} from 'hoare';

function mustBe42(num: number): void {
  if (num !== 42) {
    throw new Error('expected 42');
  }
}

test('mustBe42()', (assert) => {
  assert.equal(mustbe42(42), undefined, 'should not throw if 42');
  assert.throws(() => mustBe42(15), new Error('expected 42'), 'should throw if not 42');
});

errorsEquivalent

errorsEquivalent(err1: any, err2: any, msg?: string)

Asserts that both errors are similar. Stack traces are ignored. It checks for both non-enumerable properties (ie, name and message) and enumerable properties (anything added by extending Error).

Under the hood it uses equal() and you will get a visual diff output for any discrepancies.

Both errors must be an instance of Error, or an error will be thrown.

Tip: You may want to use throws() instead of this method for convenience, as this will catch the error for you without need to wrap it in a try/catch block.

Visual Diff Tool

Any assertion using equals() under the hood that fails will output a visual diff of the differences between the actual and expected values.

string diff string diff

Support, Feedback, and Contributions

  • Star this repo if you like it!
  • Submit an issue with your problem, feature request or bug report
  • Issue a PR against main and request review. Make sure all tests pass and coverage is good.
  • Write about unit testing best practices and use hoare in your examples

Together we can make software more reliable and easier to maintain!

License

MIT © Marc H. Weiner

See full license