1 | <div align="center">
|
2 | <h1>jest-each</h1>
|
3 | Jest Parameterised Testing
|
4 | </div>
|
5 |
|
6 | <hr />
|
7 |
|
8 | [![version](https://img.shields.io/npm/v/jest-each.svg?style=flat-square)](https://www.npmjs.com/package/jest-each) [![downloads](https://img.shields.io/npm/dm/jest-each.svg?style=flat-square)](http://npm-stat.com/charts.html?package=jest-each&from=2017-03-21) [![MIT License](https://img.shields.io/npm/l/jest-each.svg?style=flat-square)](https://github.com/facebook/jest/blob/master/LICENSE)
|
9 |
|
10 | A parameterised testing library for [Jest](https://jestjs.io/) inspired by [mocha-each](https://github.com/ryym/mocha-each).
|
11 |
|
12 | 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.
|
13 |
|
14 | ## Features
|
15 |
|
16 | - `.test` to runs multiple tests with parameterised data
|
17 | - Also under the alias: `.it`
|
18 | - `.test.only` to only run the parameterised tests
|
19 | - Also under the aliases: `.it.only` or `.fit`
|
20 | - `.test.skip` to skip the parameterised tests
|
21 | - Also under the aliases: `.it.skip` or `.xit` or `.xtest`
|
22 | - `.describe` to runs test suites with parameterised data
|
23 | - `.describe.only` to only run the parameterised suite of tests
|
24 | - Also under the aliases: `.fdescribe`
|
25 | - `.describe.skip` to skip the parameterised suite of tests
|
26 | - Also under the aliases: `.xdescribe`
|
27 | - Asynchronous tests with `done`
|
28 | - Unique test titles with [`printf` formatting](https://nodejs.org/api/util.html#util_util_format_format_args):
|
29 | - `%p` - [pretty-format](https://www.npmjs.com/package/pretty-format).
|
30 | - `%s`- String.
|
31 | - `%d`- Number.
|
32 | - `%i` - Integer.
|
33 | - `%f` - Floating point value.
|
34 | - `%j` - JSON.
|
35 | - `%o` - Object.
|
36 | - `%#` - Index of the test case.
|
37 | - `%%` - single percent sign ('%'). This does not consume an argument.
|
38 | - 🖖 Spock like data tables with [Tagged Template Literals](#tagged-template-literal-of-rows)
|
39 |
|
40 | ---
|
41 |
|
42 | - [Demo](#demo)
|
43 | - [Installation](#installation)
|
44 | - [Importing](#importing)
|
45 | - APIs
|
46 | - [Array of Rows](#array-of-rows)
|
47 | - [Usage](#usage)
|
48 | - [Tagged Template Literal of rows](#tagged-template-literal-of-rows)
|
49 | - [Usage](#usage-1)
|
50 |
|
51 | ## Demo
|
52 |
|
53 | #### Tests without jest-each
|
54 |
|
55 | ![Current jest tests](assets/default-demo.gif)
|
56 |
|
57 | #### Tests can be re-written with jest-each to:
|
58 |
|
59 | **`.test`**
|
60 |
|
61 | ![Current jest tests](assets/test-demo.gif)
|
62 |
|
63 | **`.test` with Tagged Template Literals**
|
64 |
|
65 | ![Current jest tests](assets/tagged-template-literal.gif)
|
66 |
|
67 | **`.describe`**
|
68 |
|
69 | ![Current jest tests](assets/describe-demo.gif)
|
70 |
|
71 | ## Installation
|
72 |
|
73 | `npm i --save-dev jest-each`
|
74 |
|
75 | `yarn add -D jest-each`
|
76 |
|
77 | ## Importing
|
78 |
|
79 | jest-each is a default export so it can be imported with whatever name you like.
|
80 |
|
81 | ```js
|
82 | // es6
|
83 | import each from 'jest-each';
|
84 | ```
|
85 |
|
86 | ```js
|
87 | // es5
|
88 | const each = require('jest-each');
|
89 | ```
|
90 |
|
91 | ## Array of rows
|
92 |
|
93 | ### API
|
94 |
|
95 | #### `each([parameters]).test(name, testFn)`
|
96 |
|
97 | ##### `each`:
|
98 |
|
99 | - parameters: `Array` of Arrays with the arguments that are passed into the `testFn` for each row
|
100 | - _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]]`
|
101 |
|
102 | ##### `.test`:
|
103 |
|
104 | - name: `String` the title of the `test`.
|
105 | - Generate unique test titles by positionally injecting parameters with [`printf` formatting](https://nodejs.org/api/util.html#util_util_format_format_args):
|
106 | - `%p` - [pretty-format](https://www.npmjs.com/package/pretty-format).
|
107 | - `%s`- String.
|
108 | - `%d`- Number.
|
109 | - `%i` - Integer.
|
110 | - `%f` - Floating point value.
|
111 | - `%j` - JSON.
|
112 | - `%o` - Object.
|
113 | - `%#` - Index of the test case.
|
114 | - `%%` - single percent sign ('%'). This does not consume an argument.
|
115 | - testFn: `Function` the test logic, this is the function that will receive the parameters of each row as function arguments
|
116 |
|
117 | #### `each([parameters]).describe(name, suiteFn)`
|
118 |
|
119 | ##### `each`:
|
120 |
|
121 | - parameters: `Array` of Arrays with the arguments that are passed into the `suiteFn` for each row
|
122 | - _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]]`
|
123 |
|
124 | ##### `.describe`:
|
125 |
|
126 | - name: `String` the title of the `describe`
|
127 | - Generate unique test titles by positionally injecting parameters with [`printf` formatting](https://nodejs.org/api/util.html#util_util_format_format_args):
|
128 | - `%p` - [pretty-format](https://www.npmjs.com/package/pretty-format).
|
129 | - `%s`- String.
|
130 | - `%d`- Number.
|
131 | - `%i` - Integer.
|
132 | - `%f` - Floating point value.
|
133 | - `%j` - JSON.
|
134 | - `%o` - Object.
|
135 | - `%#` - Index of the test case.
|
136 | - `%%` - single percent sign ('%'). This does not consume an argument.
|
137 | - 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
|
138 |
|
139 | ### Usage
|
140 |
|
141 | #### `.test(name, fn)`
|
142 |
|
143 | Alias: `.it(name, fn)`
|
144 |
|
145 | ```js
|
146 | each([[1, 1, 2], [1, 2, 3], [2, 1, 3]]).test(
|
147 | 'returns the result of adding %d to %d',
|
148 | (a, b, expected) => {
|
149 | expect(a + b).toBe(expected);
|
150 | },
|
151 | );
|
152 | ```
|
153 |
|
154 | #### `.test.only(name, fn)`
|
155 |
|
156 | Aliases: `.it.only(name, fn)` or `.fit(name, fn)`
|
157 |
|
158 | ```js
|
159 | each([[1, 1, 2], [1, 2, 3], [2, 1, 3]]).test.only(
|
160 | 'returns the result of adding %d to %d',
|
161 | (a, b, expected) => {
|
162 | expect(a + b).toBe(expected);
|
163 | },
|
164 | );
|
165 | ```
|
166 |
|
167 | #### `.test.skip(name, fn)`
|
168 |
|
169 | Aliases: `.it.skip(name, fn)` or `.xit(name, fn)` or `.xtest(name, fn)`
|
170 |
|
171 | ```js
|
172 | each([[1, 1, 2][(1, 2, 3)], [2, 1, 3]]).test.skip(
|
173 | 'returns the result of adding %d to %d',
|
174 | (a, b, expected) => {
|
175 | expect(a + b).toBe(expected);
|
176 | },
|
177 | );
|
178 | ```
|
179 |
|
180 | #### Asynchronous `.test(name, fn(done))`
|
181 |
|
182 | Alias: `.it(name, fn(done))`
|
183 |
|
184 | ```js
|
185 | each([['hello'], ['mr'], ['spy']]).test(
|
186 | 'gives 007 secret message: %s',
|
187 | (str, done) => {
|
188 | const asynchronousSpy = message => {
|
189 | expect(message).toBe(str);
|
190 | done();
|
191 | };
|
192 | callSomeAsynchronousFunction(asynchronousSpy)(str);
|
193 | },
|
194 | );
|
195 | ```
|
196 |
|
197 | #### `.describe(name, fn)`
|
198 |
|
199 | ```js
|
200 | each([[1, 1, 2], [1, 2, 3], [2, 1, 3]]).describe(
|
201 | '.add(%d, %d)',
|
202 | (a, b, expected) => {
|
203 | test(`returns ${expected}`, () => {
|
204 | expect(a + b).toBe(expected);
|
205 | });
|
206 |
|
207 | test('does not mutate first arg', () => {
|
208 | a + b;
|
209 | expect(a).toBe(a);
|
210 | });
|
211 |
|
212 | test('does not mutate second arg', () => {
|
213 | a + b;
|
214 | expect(b).toBe(b);
|
215 | });
|
216 | },
|
217 | );
|
218 | ```
|
219 |
|
220 | #### `.describe.only(name, fn)`
|
221 |
|
222 | Aliases: `.fdescribe(name, fn)`
|
223 |
|
224 | ```js
|
225 | each([[1, 1, 2], [1, 2, 3], [2, 1, 3]]).describe.only(
|
226 | '.add(%d, %d)',
|
227 | (a, b, expected) => {
|
228 | test(`returns ${expected}`, () => {
|
229 | expect(a + b).toBe(expected);
|
230 | });
|
231 | },
|
232 | );
|
233 | ```
|
234 |
|
235 | #### `.describe.skip(name, fn)`
|
236 |
|
237 | Aliases: `.xdescribe(name, fn)`
|
238 |
|
239 | ```js
|
240 | each([[1, 1, 2], [1, 2, 3], [2, 1, 3]]).describe.skip(
|
241 | '.add(%d, %d)',
|
242 | (a, b, expected) => {
|
243 | test(`returns ${expected}`, () => {
|
244 | expect(a + b).toBe(expected);
|
245 | });
|
246 | },
|
247 | );
|
248 | ```
|
249 |
|
250 | ---
|
251 |
|
252 | ## Tagged Template Literal of rows
|
253 |
|
254 | ### API
|
255 |
|
256 | #### `each[tagged template].test(name, suiteFn)`
|
257 |
|
258 | ```js
|
259 | each`
|
260 | a | b | expected
|
261 | ${1} | ${1} | ${2}
|
262 | ${1} | ${2} | ${3}
|
263 | ${2} | ${1} | ${3}
|
264 | `.test('returns $expected when adding $a to $b', ({a, b, expected}) => {
|
265 | expect(a + b).toBe(expected);
|
266 | });
|
267 | ```
|
268 |
|
269 | ##### `each` takes a tagged template string with:
|
270 |
|
271 | - First row of variable name column headings seperated with `|`
|
272 | - One or more subsequent rows of data supplied as template literal expressions using `${value}` syntax.
|
273 |
|
274 | ##### `.test`:
|
275 |
|
276 | - 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
|
277 | - To inject nested object values use you can supply a keyPath i.e. `$variable.path.to.value`
|
278 | - testFn: `Function` the test logic, this is the function that will receive the parameters of each row as function arguments
|
279 |
|
280 | #### `each[tagged template].describe(name, suiteFn)`
|
281 |
|
282 | ```js
|
283 | each`
|
284 | a | b | expected
|
285 | ${1} | ${1} | ${2}
|
286 | ${1} | ${2} | ${3}
|
287 | ${2} | ${1} | ${3}
|
288 | `.describe('$a + $b', ({a, b, expected}) => {
|
289 | test(`returns ${expected}`, () => {
|
290 | expect(a + b).toBe(expected);
|
291 | });
|
292 |
|
293 | test('does not mutate first arg', () => {
|
294 | a + b;
|
295 | expect(a).toBe(a);
|
296 | });
|
297 |
|
298 | test('does not mutate second arg', () => {
|
299 | a + b;
|
300 | expect(b).toBe(b);
|
301 | });
|
302 | });
|
303 | ```
|
304 |
|
305 | ##### `each` takes a tagged template string with:
|
306 |
|
307 | - First row of variable name column headings seperated with `|`
|
308 | - One or more subsequent rows of data supplied as template literal expressions using `${value}` syntax.
|
309 |
|
310 | ##### `.describe`:
|
311 |
|
312 | - 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
|
313 | - To inject nested object values use you can supply a keyPath i.e. `$variable.path.to.value`
|
314 | - 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
|
315 |
|
316 | ### Usage
|
317 |
|
318 | #### `.test(name, fn)`
|
319 |
|
320 | Alias: `.it(name, fn)`
|
321 |
|
322 | ```js
|
323 | each`
|
324 | a | b | expected
|
325 | ${1} | ${1} | ${2}
|
326 | ${1} | ${2} | ${3}
|
327 | ${2} | ${1} | ${3}
|
328 | `.test('returns $expected when adding $a to $b', ({a, b, expected}) => {
|
329 | expect(a + b).toBe(expected);
|
330 | });
|
331 | ```
|
332 |
|
333 | #### `.test.only(name, fn)`
|
334 |
|
335 | Aliases: `.it.only(name, fn)` or `.fit(name, fn)`
|
336 |
|
337 | ```js
|
338 | each`
|
339 | a | b | expected
|
340 | ${1} | ${1} | ${2}
|
341 | ${1} | ${2} | ${3}
|
342 | ${2} | ${1} | ${3}
|
343 | `.test.only('returns $expected when adding $a to $b', ({a, b, expected}) => {
|
344 | expect(a + b).toBe(expected);
|
345 | });
|
346 | ```
|
347 |
|
348 | #### `.test.skip(name, fn)`
|
349 |
|
350 | Aliases: `.it.skip(name, fn)` or `.xit(name, fn)` or `.xtest(name, fn)`
|
351 |
|
352 | ```js
|
353 | each`
|
354 | a | b | expected
|
355 | ${1} | ${1} | ${2}
|
356 | ${1} | ${2} | ${3}
|
357 | ${2} | ${1} | ${3}
|
358 | `.test.skip('returns $expected when adding $a to $b', ({a, b, expected}) => {
|
359 | expect(a + b).toBe(expected);
|
360 | });
|
361 | ```
|
362 |
|
363 | #### Asynchronous `.test(name, fn(done))`
|
364 |
|
365 | Alias: `.it(name, fn(done))`
|
366 |
|
367 | ```js
|
368 | each`
|
369 | str
|
370 | ${'hello'}
|
371 | ${'mr'}
|
372 | ${'spy'}
|
373 | `.test('gives 007 secret message: $str', ({str}, done) => {
|
374 | const asynchronousSpy = message => {
|
375 | expect(message).toBe(str);
|
376 | done();
|
377 | };
|
378 | callSomeAsynchronousFunction(asynchronousSpy)(str);
|
379 | });
|
380 | ```
|
381 |
|
382 | #### `.describe(name, fn)`
|
383 |
|
384 | ```js
|
385 | each`
|
386 | a | b | expected
|
387 | ${1} | ${1} | ${2}
|
388 | ${1} | ${2} | ${3}
|
389 | ${2} | ${1} | ${3}
|
390 | `.describe('$a + $b', ({a, b, expected}) => {
|
391 | test(`returns ${expected}`, () => {
|
392 | expect(a + b).toBe(expected);
|
393 | });
|
394 |
|
395 | test('does not mutate first arg', () => {
|
396 | a + b;
|
397 | expect(a).toBe(a);
|
398 | });
|
399 |
|
400 | test('does not mutate second arg', () => {
|
401 | a + b;
|
402 | expect(b).toBe(b);
|
403 | });
|
404 | });
|
405 | ```
|
406 |
|
407 | #### `.describe.only(name, fn)`
|
408 |
|
409 | Aliases: `.fdescribe(name, fn)`
|
410 |
|
411 | ```js
|
412 | each`
|
413 | a | b | expected
|
414 | ${1} | ${1} | ${2}
|
415 | ${1} | ${2} | ${3}
|
416 | ${2} | ${1} | ${3}
|
417 | `.describe.only('$a + $b', ({a, b, expected}) => {
|
418 | test(`returns ${expected}`, () => {
|
419 | expect(a + b).toBe(expected);
|
420 | });
|
421 | });
|
422 | ```
|
423 |
|
424 | #### `.describe.skip(name, fn)`
|
425 |
|
426 | Aliases: `.xdescribe(name, fn)`
|
427 |
|
428 | ```js
|
429 | each`
|
430 | a | b | expected
|
431 | ${1} | ${1} | ${2}
|
432 | ${1} | ${2} | ${3}
|
433 | ${2} | ${1} | ${3}
|
434 | `.describe.skip('$a + $b', ({a, b, expected}) => {
|
435 | test(`returns ${expected}`, () => {
|
436 | expect(a + b).toBe(expected);
|
437 | });
|
438 | });
|
439 | ```
|
440 |
|
441 | ## License
|
442 |
|
443 | MIT
|