Added test to ensure that release notes are parsed properly Updated to latest dependencies to fix security vulnerability Updated GH Actions Workflow to run test before updating tag Updated README.md to reflect proper whatsnew documentation.
444 lines
11 KiB
Markdown
444 lines
11 KiB
Markdown
<div align="center">
|
|
<h1>jest-each</h1>
|
|
Jest Parameterised Testing
|
|
</div>
|
|
|
|
<hr />
|
|
|
|
[](https://www.npmjs.com/package/jest-each) [](http://npm-stat.com/charts.html?package=jest-each&from=2017-03-21) [](https://github.com/facebook/jest/blob/master/LICENSE)
|
|
|
|
A parameterised testing library for [Jest](https://jestjs.io/) inspired by [mocha-each](https://github.com/ryym/mocha-each).
|
|
|
|
jest-each allows you to provide multiple arguments to your `test`/`describe` which results in the test/suite being run once per row of parameters.
|
|
|
|
## Features
|
|
|
|
- `.test` to runs multiple tests with parameterised data
|
|
- Also under the alias: `.it`
|
|
- `.test.only` to only run the parameterised tests
|
|
- Also under the aliases: `.it.only` or `.fit`
|
|
- `.test.skip` to skip the parameterised tests
|
|
- Also under the aliases: `.it.skip` or `.xit` or `.xtest`
|
|
- `.describe` to runs test suites with parameterised data
|
|
- `.describe.only` to only run the parameterised suite of tests
|
|
- Also under the aliases: `.fdescribe`
|
|
- `.describe.skip` to skip the parameterised suite of tests
|
|
- Also under the aliases: `.xdescribe`
|
|
- Asynchronous tests with `done`
|
|
- Unique test titles with [`printf` formatting](https://nodejs.org/api/util.html#util_util_format_format_args):
|
|
- `%p` - [pretty-format](https://www.npmjs.com/package/pretty-format).
|
|
- `%s`- String.
|
|
- `%d`- Number.
|
|
- `%i` - Integer.
|
|
- `%f` - Floating point value.
|
|
- `%j` - JSON.
|
|
- `%o` - Object.
|
|
- `%#` - Index of the test case.
|
|
- `%%` - single percent sign ('%'). This does not consume an argument.
|
|
- 🖖 Spock like data tables with [Tagged Template Literals](#tagged-template-literal-of-rows)
|
|
|
|
---
|
|
|
|
- [Demo](#demo)
|
|
- [Installation](#installation)
|
|
- [Importing](#importing)
|
|
- APIs
|
|
- [Array of Rows](#array-of-rows)
|
|
- [Usage](#usage)
|
|
- [Tagged Template Literal of rows](#tagged-template-literal-of-rows)
|
|
- [Usage](#usage-1)
|
|
|
|
## Demo
|
|
|
|
#### Tests without jest-each
|
|
|
|

|
|
|
|
#### Tests can be re-written with jest-each to:
|
|
|
|
**`.test`**
|
|
|
|

|
|
|
|
**`.test` with Tagged Template Literals**
|
|
|
|

|
|
|
|
**`.describe`**
|
|
|
|

|
|
|
|
## Installation
|
|
|
|
`npm i --save-dev jest-each`
|
|
|
|
`yarn add -D jest-each`
|
|
|
|
## Importing
|
|
|
|
jest-each is a default export so it can be imported with whatever name you like.
|
|
|
|
```js
|
|
// es6
|
|
import each from 'jest-each';
|
|
```
|
|
|
|
```js
|
|
// es5
|
|
const each = require('jest-each').default;
|
|
```
|
|
|
|
## Array of rows
|
|
|
|
### API
|
|
|
|
#### `each([parameters]).test(name, testFn)`
|
|
|
|
##### `each`:
|
|
|
|
- parameters: `Array` of Arrays with the arguments that are passed into the `testFn` for each row
|
|
- _Note_ If you pass in a 1D array of primitives, internally it will be mapped to a table i.e. `[1, 2, 3] -> [[1], [2], [3]]`
|
|
|
|
##### `.test`:
|
|
|
|
- name: `String` the title of the `test`.
|
|
- Generate unique test titles by positionally injecting parameters with [`printf` formatting](https://nodejs.org/api/util.html#util_util_format_format_args):
|
|
- `%p` - [pretty-format](https://www.npmjs.com/package/pretty-format).
|
|
- `%s`- String.
|
|
- `%d`- Number.
|
|
- `%i` - Integer.
|
|
- `%f` - Floating point value.
|
|
- `%j` - JSON.
|
|
- `%o` - Object.
|
|
- `%#` - Index of the test case.
|
|
- `%%` - single percent sign ('%'). This does not consume an argument.
|
|
- testFn: `Function` the test logic, this is the function that will receive the parameters of each row as function arguments
|
|
|
|
#### `each([parameters]).describe(name, suiteFn)`
|
|
|
|
##### `each`:
|
|
|
|
- parameters: `Array` of Arrays with the arguments that are passed into the `suiteFn` for each row
|
|
- _Note_ If you pass in a 1D array of primitives, internally it will be mapped to a table i.e. `[1, 2, 3] -> [[1], [2], [3]]`
|
|
|
|
##### `.describe`:
|
|
|
|
- name: `String` the title of the `describe`
|
|
- Generate unique test titles by positionally injecting parameters with [`printf` formatting](https://nodejs.org/api/util.html#util_util_format_format_args):
|
|
- `%p` - [pretty-format](https://www.npmjs.com/package/pretty-format).
|
|
- `%s`- String.
|
|
- `%d`- Number.
|
|
- `%i` - Integer.
|
|
- `%f` - Floating point value.
|
|
- `%j` - JSON.
|
|
- `%o` - Object.
|
|
- `%#` - Index of the test case.
|
|
- `%%` - single percent sign ('%'). This does not consume an argument.
|
|
- suiteFn: `Function` the suite of `test`/`it`s to be ran, this is the function that will receive the parameters in each row as function arguments
|
|
|
|
### Usage
|
|
|
|
#### `.test(name, fn)`
|
|
|
|
Alias: `.it(name, fn)`
|
|
|
|
```js
|
|
each([[1, 1, 2], [1, 2, 3], [2, 1, 3]]).test(
|
|
'returns the result of adding %d to %d',
|
|
(a, b, expected) => {
|
|
expect(a + b).toBe(expected);
|
|
},
|
|
);
|
|
```
|
|
|
|
#### `.test.only(name, fn)`
|
|
|
|
Aliases: `.it.only(name, fn)` or `.fit(name, fn)`
|
|
|
|
```js
|
|
each([[1, 1, 2], [1, 2, 3], [2, 1, 3]]).test.only(
|
|
'returns the result of adding %d to %d',
|
|
(a, b, expected) => {
|
|
expect(a + b).toBe(expected);
|
|
},
|
|
);
|
|
```
|
|
|
|
#### `.test.skip(name, fn)`
|
|
|
|
Aliases: `.it.skip(name, fn)` or `.xit(name, fn)` or `.xtest(name, fn)`
|
|
|
|
```js
|
|
each([[1, 1, 2], [1, 2, 3], [2, 1, 3]]).test.skip(
|
|
'returns the result of adding %d to %d',
|
|
(a, b, expected) => {
|
|
expect(a + b).toBe(expected);
|
|
},
|
|
);
|
|
```
|
|
|
|
#### Asynchronous `.test(name, fn(done))`
|
|
|
|
Alias: `.it(name, fn(done))`
|
|
|
|
```js
|
|
each([['hello'], ['mr'], ['spy']]).test(
|
|
'gives 007 secret message: %s',
|
|
(str, done) => {
|
|
const asynchronousSpy = message => {
|
|
expect(message).toBe(str);
|
|
done();
|
|
};
|
|
callSomeAsynchronousFunction(asynchronousSpy)(str);
|
|
},
|
|
);
|
|
```
|
|
|
|
#### `.describe(name, fn)`
|
|
|
|
```js
|
|
each([[1, 1, 2], [1, 2, 3], [2, 1, 3]]).describe(
|
|
'.add(%d, %d)',
|
|
(a, b, expected) => {
|
|
test(`returns ${expected}`, () => {
|
|
expect(a + b).toBe(expected);
|
|
});
|
|
|
|
test('does not mutate first arg', () => {
|
|
a + b;
|
|
expect(a).toBe(a);
|
|
});
|
|
|
|
test('does not mutate second arg', () => {
|
|
a + b;
|
|
expect(b).toBe(b);
|
|
});
|
|
},
|
|
);
|
|
```
|
|
|
|
#### `.describe.only(name, fn)`
|
|
|
|
Aliases: `.fdescribe(name, fn)`
|
|
|
|
```js
|
|
each([[1, 1, 2], [1, 2, 3], [2, 1, 3]]).describe.only(
|
|
'.add(%d, %d)',
|
|
(a, b, expected) => {
|
|
test(`returns ${expected}`, () => {
|
|
expect(a + b).toBe(expected);
|
|
});
|
|
},
|
|
);
|
|
```
|
|
|
|
#### `.describe.skip(name, fn)`
|
|
|
|
Aliases: `.xdescribe(name, fn)`
|
|
|
|
```js
|
|
each([[1, 1, 2], [1, 2, 3], [2, 1, 3]]).describe.skip(
|
|
'.add(%d, %d)',
|
|
(a, b, expected) => {
|
|
test(`returns ${expected}`, () => {
|
|
expect(a + b).toBe(expected);
|
|
});
|
|
},
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## Tagged Template Literal of rows
|
|
|
|
### API
|
|
|
|
#### `each[tagged template].test(name, suiteFn)`
|
|
|
|
```js
|
|
each`
|
|
a | b | expected
|
|
${1} | ${1} | ${2}
|
|
${1} | ${2} | ${3}
|
|
${2} | ${1} | ${3}
|
|
`.test('returns $expected when adding $a to $b', ({a, b, expected}) => {
|
|
expect(a + b).toBe(expected);
|
|
});
|
|
```
|
|
|
|
##### `each` takes a tagged template string with:
|
|
|
|
- First row of variable name column headings seperated with `|`
|
|
- One or more subsequent rows of data supplied as template literal expressions using `${value}` syntax.
|
|
|
|
##### `.test`:
|
|
|
|
- name: `String` the title of the `test`, use `$variable` in the name string to inject test values into the test title from the tagged template expressions
|
|
- To inject nested object values use you can supply a keyPath i.e. `$variable.path.to.value`
|
|
- testFn: `Function` the test logic, this is the function that will receive the parameters of each row as function arguments
|
|
|
|
#### `each[tagged template].describe(name, suiteFn)`
|
|
|
|
```js
|
|
each`
|
|
a | b | expected
|
|
${1} | ${1} | ${2}
|
|
${1} | ${2} | ${3}
|
|
${2} | ${1} | ${3}
|
|
`.describe('$a + $b', ({a, b, expected}) => {
|
|
test(`returns ${expected}`, () => {
|
|
expect(a + b).toBe(expected);
|
|
});
|
|
|
|
test('does not mutate first arg', () => {
|
|
a + b;
|
|
expect(a).toBe(a);
|
|
});
|
|
|
|
test('does not mutate second arg', () => {
|
|
a + b;
|
|
expect(b).toBe(b);
|
|
});
|
|
});
|
|
```
|
|
|
|
##### `each` takes a tagged template string with:
|
|
|
|
- First row of variable name column headings seperated with `|`
|
|
- One or more subsequent rows of data supplied as template literal expressions using `${value}` syntax.
|
|
|
|
##### `.describe`:
|
|
|
|
- name: `String` the title of the `test`, use `$variable` in the name string to inject test values into the test title from the tagged template expressions
|
|
- To inject nested object values use you can supply a keyPath i.e. `$variable.path.to.value`
|
|
- suiteFn: `Function` the suite of `test`/`it`s to be ran, this is the function that will receive the parameters in each row as function arguments
|
|
|
|
### Usage
|
|
|
|
#### `.test(name, fn)`
|
|
|
|
Alias: `.it(name, fn)`
|
|
|
|
```js
|
|
each`
|
|
a | b | expected
|
|
${1} | ${1} | ${2}
|
|
${1} | ${2} | ${3}
|
|
${2} | ${1} | ${3}
|
|
`.test('returns $expected when adding $a to $b', ({a, b, expected}) => {
|
|
expect(a + b).toBe(expected);
|
|
});
|
|
```
|
|
|
|
#### `.test.only(name, fn)`
|
|
|
|
Aliases: `.it.only(name, fn)` or `.fit(name, fn)`
|
|
|
|
```js
|
|
each`
|
|
a | b | expected
|
|
${1} | ${1} | ${2}
|
|
${1} | ${2} | ${3}
|
|
${2} | ${1} | ${3}
|
|
`.test.only('returns $expected when adding $a to $b', ({a, b, expected}) => {
|
|
expect(a + b).toBe(expected);
|
|
});
|
|
```
|
|
|
|
#### `.test.skip(name, fn)`
|
|
|
|
Aliases: `.it.skip(name, fn)` or `.xit(name, fn)` or `.xtest(name, fn)`
|
|
|
|
```js
|
|
each`
|
|
a | b | expected
|
|
${1} | ${1} | ${2}
|
|
${1} | ${2} | ${3}
|
|
${2} | ${1} | ${3}
|
|
`.test.skip('returns $expected when adding $a to $b', ({a, b, expected}) => {
|
|
expect(a + b).toBe(expected);
|
|
});
|
|
```
|
|
|
|
#### Asynchronous `.test(name, fn(done))`
|
|
|
|
Alias: `.it(name, fn(done))`
|
|
|
|
```js
|
|
each`
|
|
str
|
|
${'hello'}
|
|
${'mr'}
|
|
${'spy'}
|
|
`.test('gives 007 secret message: $str', ({str}, done) => {
|
|
const asynchronousSpy = message => {
|
|
expect(message).toBe(str);
|
|
done();
|
|
};
|
|
callSomeAsynchronousFunction(asynchronousSpy)(str);
|
|
});
|
|
```
|
|
|
|
#### `.describe(name, fn)`
|
|
|
|
```js
|
|
each`
|
|
a | b | expected
|
|
${1} | ${1} | ${2}
|
|
${1} | ${2} | ${3}
|
|
${2} | ${1} | ${3}
|
|
`.describe('$a + $b', ({a, b, expected}) => {
|
|
test(`returns ${expected}`, () => {
|
|
expect(a + b).toBe(expected);
|
|
});
|
|
|
|
test('does not mutate first arg', () => {
|
|
a + b;
|
|
expect(a).toBe(a);
|
|
});
|
|
|
|
test('does not mutate second arg', () => {
|
|
a + b;
|
|
expect(b).toBe(b);
|
|
});
|
|
});
|
|
```
|
|
|
|
#### `.describe.only(name, fn)`
|
|
|
|
Aliases: `.fdescribe(name, fn)`
|
|
|
|
```js
|
|
each`
|
|
a | b | expected
|
|
${1} | ${1} | ${2}
|
|
${1} | ${2} | ${3}
|
|
${2} | ${1} | ${3}
|
|
`.describe.only('$a + $b', ({a, b, expected}) => {
|
|
test(`returns ${expected}`, () => {
|
|
expect(a + b).toBe(expected);
|
|
});
|
|
});
|
|
```
|
|
|
|
#### `.describe.skip(name, fn)`
|
|
|
|
Aliases: `.xdescribe(name, fn)`
|
|
|
|
```js
|
|
each`
|
|
a | b | expected
|
|
${1} | ${1} | ${2}
|
|
${1} | ${2} | ${3}
|
|
${2} | ${1} | ${3}
|
|
`.describe.skip('$a + $b', ({a, b, expected}) => {
|
|
test(`returns ${expected}`, () => {
|
|
expect(a + b).toBe(expected);
|
|
});
|
|
});
|
|
```
|
|
|
|
## License
|
|
|
|
MIT
|