@sondr3/minitest
Version:
A low-feature, dependency-free and performant test runner inspired by Rust and Deno
294 lines (217 loc) • 8.49 kB
Markdown
<h1 align="center">minitest</h1>
<p align="center">
<a href="https://github.com/sondr3/minitest/actions"><img alt="GitHub Actions Status" src="https://github.com/sondr3/minitest/workflows/pipeline/badge.svg" /></a>
<a href="https://www.npmjs.com/package/@sondr3/minitest"><img alt="NPM" src="https://badge.fury.io/js/%40sondr3%2Fminitest.svg" /></a>
</p>
<p align="center">
<b>A low-feature, dependency-free and performant test runner inspired by Rust and Deno</b>
</p>
- **Simplicity**: Use the `mt` test runner with the `test` function, nothing more.
- **Performance**: By doing and including less we can run more quick.
- **Minimal**: Bring your own assertions, snapshots, `mt` will always be dependency free.
<details>
<summary>Table of Contents</summary>
<br />
## Table of Contents
- [Quickstart](#quickstart)
- [Usage](#usage)
- [Finding tests](#finding-tests)
- [Writing tests](#writing-tests)
- [Running tests](#running-tests)
- [Filtering](#filtering)
- [Failing fast](#failing-fast)
- [Quiet](#quiet)
- [CLI](#cli)
- [Extras](#extras)
- [TypeScript](#typescript)
- [Coverage](#coverage)
- [JSX, TSX, browsers](#jsx-tsx-browsers)
- [Rationale](#rationale)
- [License](#license)
</details>
# Quickstart
1. Install via your tool of choice: `pnpm add --dev @sondr3/minitest`
2. Add a test script to `package.json`:
```json
"scripts": {
"test": "mt <dir>",
},
```
3. Write your tests:
```js
import { strict as assert } from "assert";
import { test } from "@sondr3/minitest";
test("it works!", () => {
assert(true === true, "Phew");
});
```
4. Run your tests: `pnpm test`
```text
running 1 test
running 1 test in dir/index.test.js
test it works! ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 filtered out; finished in 32ms
```
# Usage
## Finding tests
`mt` will recursively look for files in your current directory:
- named `test.{js,mjs}`,
- or ending in `.test.{js,mjs}`
- or ending in `_test.{js,mjs}`
## Writing tests
To write tests you need to import the `test` function from `@sondr3/minitest`. There
are a couple of different styles that can be used to write tests, but we'll get
into these later.
```js
import { test } from "@sondr3/minitest";
import { strict as assert } from "node:assert";
test("hello world #1", () => {
const x = 1 + 2;
assert.equal(x, 3);
});
test({ name: "hello world #2" }, () => {
const x = 1 + 2;
assert.equal(x, 3);
});
```
You can use any assertion library you want, like [Chai][chai] for a TDD/BDD
like assertions and [Sinon.JS][sinon] to spy and mock functionality. The only
requirement is that the assertions throw when they fail.
If you come from a frameworks like [Jest][jest] you may be surprised to learn
that there is no built-in support for nesting tests, or anything like `beforeAll`
or `beforeEach` in `minitest`. I can highly recommend [this][nesting] article from
Kent C. Dodds about nesting and test hooks. After having used Deno and Rust which do
not have such functionality, I don't miss it. I recommend you give it a try!
### Async functions
You can also test asynchronous code by turning the test function into a function
that returns a promise. Simply add `async` in front of the function:
```js
import { test } from "@sondr3/minitest";
import { strict as assert } from "node:assert";
test("async hello world", async () => {
const x = 1 + 2;
const wait = async () => new Promise((res) => setTimeout(res, 1000));
await wait();
assert.equal(x, 3);
});
```
## Running tests
Simply run `mt` to test all test files matching the pattern in [finding tests](#finding-tests), or
point it to the directory or file you want to run:
```shell
# Run all tests in current directory
mt
# Run all tests in the fs directory
mt fs
# Run a single test file
mt fs.test.js
```
## Filtering
There are tree different ways of filtering tests, either by filtering against test
names, skipping tests or running a select subset of tests.
### Filtering via the CLI
Tests can be filtered out by using the `--filter` option. This option accepts
either a case insensitive string or a Regex pattern as its value.
If you have the following tests:
```js
test("a-test", () => {});
test("test-1", () => {});
test("test-2", () => {});
```
You can then filter them by filtering all tests that contain the word `test`:
```shell
mt --filter "test"
```
Or by matching against a Regex pattern:
```shell
mt --filter "/test-*\d/"
```
### Ignoring (skipping)
If you want to ignore/skip tests based on some boolean condition or because they
are currently incorrect you can use the `ignore()` method or the `ignore` field
in the options:
```js
test("ignored", () => {}).ignore(); // or .ignore(condition)
test({ name: "other ignored", ignore: process.arch === "x64" }, () => {});
test("final ignored", () => {}, { ignore: true });
```
### Running specific tests
Sometimes you may want to only run a subset of your tests because you are working
on a problem where you want to ignore tests not relevant to it. To do this you can
mark tests using the `only()` method or the `only` field in the options, and only
these tests are run. While these tests will run normally and report success and
failure normally, the whole test suite will still fail as using `only` should only
be a temporary measure as it disables nearly the whole test suite.
```js
test("other ignored", () => {}).only();
test({ name: "ignored", only: true }, () => {});
test("final ignored", () => {}, { only: true });
```
## Failing fast
If you have a test suite that takes a long time to complete or you simply want
to exit after the first error, one can use the `--fail-fast` option:
```shell
# fail immediately
mt --fail-fast
# fail after three failures
mt --fail-fast 3
```
## Quiet
The test output for `mt` can quickly become verbose once the test suite grows, to
help with this one can use the `--quiet` flag to make it far less verbose:
```shell
mt --quiet
```
## CLI
For a complete overview over the available options, use the `--help` flag:
```shell
$ mt --help
minitest v0.1.0
A low-feature and performant test runner inspired by Rust and Deno
USAGE:
mt <dir> [flags]
OPTIONS:
-q, --quiet Quiet output
-f, --filter=<filter> Filter tests by name, accepts regex
-F, --fail-fast=<N> Fail after N test failures [default: 0]
-v, --version Print version
-h, --help Print help
```
# Extras
## TypeScript
There is no built-in support for natively running TypeScript files, they need to
be compiled to JavaScript first. In other words, your build step needs to happen
before you run your tests.
## Coverage
To generate coverate reporting for your tests, I highly recommend using [c8][c8]. It
generates coverage based on the built-in coverage functionality of NodeJS and will
just magically work with your tests. It is also very fast compared to similar
functionality in other testing libraries. To generate coverage by default, change
your `test` script to the following after you've added c8 as a dependency:
```json
"scripts": {
"test": "c8 mt <dir>",
},
```
## JSX, TSX, browsers
Like with TypeScript, there is no built-in support for usage with JSX and/or TSX,
and no specific functionality for working against browsers. You may be able to make
it work, but it is purpose built around testing with Node, so YMMV.
# Rationale
Why would you use this over the myriad of other test frameworks, runners and libraries
that exist? You probably shouldn't, [Jest][jest], [Ava][ava], [tape][tape], [uvu][uvu] and
a whole lot of others are more mature, stable, feature rich and has a wider userbase, but
if you want a simple Rust/Deno like test framework, this might just scratch your itch.
I also created this as an exercise in learning how testing works under the hood, and had
a lot of fun building it out into a actual useful package. For my needs and packages `minitest`
is just what I want from a test framework: minimal fuzzing, fast and easy and straight forward.
# License
MIT.
[jest]: https://jestjs.io/
[ava]: https://github.com/avajs/ava
[tape]: https://github.com/substack/tape
[uvu]: https://github.com/lukeed/uvu
[chai]: https://www.npmjs.com/package/chai
[sinon]: https://www.npmjs.com/package/sinon
[c8]: https://github.com/bcoe/c8
[nesting]: https://kentcdodds.com/blog/avoid-nesting-when-youre-testing