Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/tylors/45
A Functional, monadic test-runner
https://github.com/tylors/45
assertions functional-programming lazy monad test test-runner
Last synced: 18 days ago
JSON representation
A Functional, monadic test-runner
- Host: GitHub
- URL: https://github.com/tylors/45
- Owner: TylorS
- License: mit
- Created: 2017-01-28T21:10:46.000Z (almost 8 years ago)
- Default Branch: master
- Last Pushed: 2017-03-03T13:01:33.000Z (over 7 years ago)
- Last Synced: 2024-10-26T23:24:07.175Z (19 days ago)
- Topics: assertions, functional-programming, lazy, monad, test, test-runner
- Language: TypeScript
- Homepage:
- Size: 150 KB
- Stars: 26
- Watchers: 6
- Forks: 4
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE.md
Awesome Lists containing this project
README
# 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!
```sh
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`](https://github.com/TylorS/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
```js
// test/foo.js// ES2015
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);
})// commonjs
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
```sh
./node_modules/.bin/45 test/foo.js
# Supports globs
./node_modules/.bin/45 test/*.js# or without parameters
# by default it will search for all .test and .spec files in src/ folder
# and for all files in test/ and tests/ folders
./node_modules/.bin/45
```And you should see:
![basic-test](./.assets/basic_test.png)
## 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](#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](#types) section. Many assertions are re-exported by this
library from [4.5](https://github.com/TylorS/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`
Allows collecting many `Test`s together as a larger whole. All tests are
run in parallel.```typescript
import { describe } from '45';export const test = describe('My Thing', [ ... ])
```#### `given(parameters: string, tests: Array): 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.```typescript
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.```typescript
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.
```typescript
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`
Allow running a hook before a series of tests. This will run the containing array
of tests one after another.```typescript
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](#types) section.#### `equals(expected: A, actual: A): Assertion`
Asserts two values `expected` and `actual` to have value equality.
#### `is(expected: A, actual: A): Assertion`
Asserts two values `expected` and `actual` to have referential equality.
#### `pass(value: A): Assertion`
Creates an assertion which always passes with a given value.
#### `fail(message: any): Assertion`
Creates an assertion which will always fail with a given message.
#### `throws(fn: () => any): Assertion`
Creates an assertion that tests that a given function throws and error.
## Assertion combinators (re-exported from 4.5)
#### `map(fn: (a: A) => B, assertion: Assertion): Assertion`
Given a function it maps one assertion value to another.
```typescript
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 /* called with 1 */, equals(1, 1)) // Assertion<2>;
});
```#### `ap(fn: Assertion<(a: A) => B>, value: Assertion): Assertion`
Given an assertion containing a function from `a` to `b`, and an assertion
of `a` returns an assertion of type `b`.```typescript
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 = pass(1);return ap(fn, value); // returns an assertion containing the value 2
});
```#### `chain(fn: (a: A) => Assertion, assertion: Assertion): Assertion`
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.```typescript
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); // don't forget, all assertions are curried!
const isThree = equals(3);return chain(isThree, map(add1, chain(isTwo, map(add1, oneIsOne))));
})
```#### `bimap`
Type signature is to long for the header :smile:
```typescript
bimap(
failure: (message: string) => string,
success: (a: A) => B,
assertion: Assertion): Assertion;
```Similar to `map` but also allows adjusting error messages.
Useful for creating your own error messages.
```typescript
import { bimap, equals, fail, pass } from '45';export const test = it('allows adjusting error message', () => {
return bimap(() => 'Sadly 1 is not correct', fail /* should not pass*/, fail(1));
});
```#### `concat(one: Assertion, two: Assertion): Assertion`
Chain together 2 assertions.
```typescript
import { concat, pass, it } from '45';export const test = it('concatenates', () => {
return concat(pass(1), pass(2)); // passes if and only if both assertions pass.
});
```## Types
```typescript
// 45 interfaces
export interface Test {
name: string;
timeout: number;
run(): Promise>;
showStatus: boolean;
}export type TestFn =
(() => Assertion) |
(() => Promise>) |
(() => Observable>);export interface TestResult {
failures: number;
message: string;
}export type Observable =
{
subscribe(observer: Observer): Subscription;
}export type Observer =
{
next(value: A): any;
error(err: any): any;
complete(): any;
}export type Subscription =
{
unsubscribe(): any;
}// 4.5 re-exported interfaces
export interface Assertion {
verify(verification: Verification): void;
}export interface Verification {
success(actual: T): any;
failure(message: string): any;
}
```