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');
 | |
| ```
 | |
| 
 | |
| ## 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
 |