45
A functionally-oriented test runner
45
is the fast and functional test runner that is easy to use and gets out of
your way.
Let me have it!
npm install --save-dev 45
Features
- Does not rely on globals
- Test failure if no assertions are returned
- Lazy, monadic, and curried test assertions via
4.5
- Promise, Observable, and Async/Await support
- Runs all tests in parallel
- ES2015 and TypeScript support out-of-box
Basic Usage
Create a test file
import { describe, given, it, equals } from '45';
export const test = describe('Array', [
given('a few numbers', [
it('has length greater than 0', () => {
return equals([1, 2, 3].length > 0, true);
})
])
])
export const otherTest = it('equals 4', () => {
return equals(4, 4);
})
const { describe, given, it, equals } = require('45');
exports.test = describe('Array', [
given('a few numbers', [
it('has length greater than 0', () => {
return equals([1, 2, 3].length > 0, true);
})
])
])
exports.otherTest = it('equals 4', () => {
return equals(4, 4);
})
In your terminal run
./node_modules/.bin/45 test/foo.js
./node_modules/.bin/45 test/*.js
./node_modules/.bin/45
And you should see:
Tests
When running 45 from the command line, it will look for all test files
it can, collecting all exports, of no particular export name, that adhere
to the Test
interface described in the types section. This means
that 45 can be extended to handle new test types not offered here via 3rd
party libraries.
All 45 Test
s must return objects adhering to the Assertion
interface
described in the Types section. Many assertions are re-exported by this
library from 4.5. Though a number are provided
by default -- 3rd party libraries implementing the Assertion
interface can be used
100% freely.
describe(thing: string, tests: Array<Test>): Test
Allows collecting many Test
s together as a larger whole. All tests are
run in parallel.
import { describe } from '45';
export const test = describe('My Thing', [ ... ])
given(parameters: string, tests: Array<Test>): Test
Just like describe
in that it allows collecting many Test
s together as a
larger whole, but allows for more descriptive test suites. All tests are run in
parallel.
import { describe, given } from '45';
export const test = describe('My thing', [
given(`a b and c`, [
it('does ...', () => { ... })
])
])
it(does: string, testFn: TestFn): Test
Primitive test type which allows providing a callback to
actually perform assertions. By default, will timeout at 2000 milliseconds.
import { it, pass } from '45';
export const test = it('does things', () => {
return pass(1);
})
timeout(ms: number, test: Test): Test
Allows adjusting the amount of time a test can take to complete.
import { it, timeout, equals } from '45';
export const failing = it('fails', () => {
return new Promise((resolve) => {
setTimeout(resolve, 3000, 1)
})
.then(equals(1));
})
export const passing = timeout(3500, it('passes', () => {
return new Promise((resolve) => {
setTimeout(resolve, 3000, 1)
})
.then(equals(1));
}));
beforeEach(hook: () => any, tests: Array<Test>): Test
Allow running a hook before a series of tests. This will run the containing array
of tests one after another.
import { beforeEach, describe, given, it } from './';
import { equals } from '4.5';
let x = 0;
export const test = describe('beforeEach', [
given(`a function and an array of tests`, [
beforeEach(() => { x++; }, [
it('runs beforeEach test', () => {
return equals(1, x);
}),
it('runs beforeEach test every time', () => {
return equals(2, x);
}),
]),
]),
]);
Assertions (re-exported from 4.5)
- All functions of arity 2 or more are curried.
- All types are defined below in the types section.
equals<A>(expected: A, actual: A): Assertion<A>
Asserts two values expected
and actual
to have value equality.
is<A>(expected: A, actual: A): Assertion<A>
Asserts two values expected
and actual
to have referential equality.
pass<A>(value: A): Assertion<A>
Creates an assertion which always passes with a given value.
fail(message: any): Assertion<void>
Creates an assertion which will always fail with a given message.
throws(fn: () => any): Assertion<Error>
Creates an assertion that tests that a given function throws and error.
Assertion combinators (re-exported from 4.5)
map<A, B>(fn: (a: A) => B, assertion: Assertion<A>): Assertion<B>
Given a function it maps one assertion value to another.
import { it, map, equals } from '45';
export const test = it('maps a value from type A to type B', () => {
const add1 = (x: number) => x + 1;
return map(add1 , equals(1, 1))
});
ap<A, B>(fn: Assertion<(a: A) => B>, value: Assertion<A>): Assertion<B>
Given an assertion containing a function from a
to b
, and an assertion
of a
returns an assertion of type b
.
import { it, ap, pass } from '45';
export const test = it('applys fn to a value', () => {
const add1 = (x: number) => x + 1;
const fn: Assertion<(x: number) => number> = pass(add1);
const value: Assertion<number> = pass(1);
return ap(fn, value);
});
chain<A, B>(fn: (a: A) => Assertion<B>, assertion: Assertion<A>): Assertion<B>
Given a function from one value a to Assertion b and an assertion of type a, returns an assertion of type b
Useful for making many assertions using the values from the previous assertion.
import { it, chain, equals, map }
export const test = it('chains many assertions', () => {
const add1 = (x: number) => x + 1;
const oneIsOne = equals(1, 1);
const isTwo = equals(2);
const isThree = equals(3);
return chain(isThree, map(add1, chain(isTwo, map(add1, oneIsOne))));
})
bimap
Type signature is to long for the header :smile:
bimap<A, B>(
failure: (message: string) => string,
success: (a: A) => B,
assertion: Assertion<A>): Assertion<B>;
Similar to map
but also allows adjusting error messages.
Useful for creating your own error messages.
import { bimap, equals, fail, pass } from '45';
export const test = it('allows adjusting error message', () => {
return bimap(() => 'Sadly 1 is not correct', fail , fail(1));
});
concat(one: Assertion<A>, two: Assertion<A>): Assertion<A>
Chain together 2 assertions.
import { concat, pass, it } from '45';
export const test = it('concatenates', () => {
return concat(pass(1), pass(2));
});
Types
export interface Test {
name: string;
timeout: number;
run(): Promise<Assertion<any>>;
showStatus: boolean;
}
export type TestFn =
(() => Assertion<any>) |
(() => Promise<Assertion<any>>) |
(() => Observable<Assertion<any>>);
export interface TestResult {
failures: number;
message: string;
}
export type Observable<A> =
{
subscribe(observer: Observer<A>): Subscription;
}
export type Observer<A> =
{
next(value: A): any;
error(err: any): any;
complete(): any;
}
export type Subscription =
{
unsubscribe(): any;
}
export interface Assertion<T> {
verify(verification: Verification<T>): void;
}
export interface Verification<T> {
success(actual: T): any;
failure(message: string): any;
}