Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support concurrent in Jest Each #9326

Merged
merged 10 commits into from Aug 6, 2020
164 changes: 164 additions & 0 deletions docs/GlobalAPI.md
Expand Up @@ -465,6 +465,170 @@ test('has lemon in it', () => {

Even though the call to `test` will return right away, the test doesn't complete until the promise resolves as well.

### `test.concurrent(name, fn, timeout)`
Copy link
Member

@SimenB SimenB Aug 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had forgotten this is undocumented... Can you add something like

Note: test.concurrent is considered experimental - see https://github.com/facebook/jest/labels/Area%3A%20Concurrent for details on missing features and other issues


Also under the alias: `it.concurrent(name, fn, timeout)`

Use `test.concurrent` if you want the test to run concurrently.

The first argument is the test name; the second argument is an asynchronous function that contains the expectations to test. The third argument (optional) is `timeout` (in milliseconds) for specifying how long to wait before aborting. _Note: The default timeout is 5 seconds._

```
test.concurrent('addition of 2 numbers', async () => {
expect(5 + 3).toBe(8);
});

test.concurrent('subtraction 2 numbers', async () => {
expect(5 - 3).toBe(2);
});
```

> Note: Use `maxConcurrency` in configuration to prevents Jest from executing more than the specified amount of tests at the same time

### `test.concurrent.each(table)(name, fn, timeout)`

Also under the alias: `it.concurrent.each(table)(name, fn, timeout)`

Use `test.concurrent.each` if you keep duplicating the same test with different data. `test.each` allows you to write the test once and pass data in, the tests are all run asynchronously.

`test.concurrent.each` is available with two APIs:

#### 1. `test.concurrent.each(table)(name, fn, timeout)`

- `table`: `Array` of Arrays with the arguments that are passed into the test `fn` 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]]`
- `name`: `String` the title of the test block.
- 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.
- `fn`: `Function` the test to be ran, this is the function that will receive the parameters in each row as function arguments, **this will have to be an asynchronous function**.
- Optionally, you can provide a `timeout` (in milliseconds) for specifying how long to wait for each row before aborting. _Note: The default timeout is 5 seconds._

Example:

```js
test.concurrent.each([
[1, 1, 2],
[1, 2, 3],
[2, 1, 3],
])('.add(%i, %i)', (a, b, expected) => {
expect(a + b).toBe(expected);
});
```

#### 2. `` test.each`table`(name, fn, timeout) ``

- `table`: `Tagged Template Literal`
- First row of variable name column headings separated with `|`
- One or more subsequent rows of data supplied as template literal expressions using `${value}` syntax.
- `name`: `String` the title of the test, use `$variable` to inject test data 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`
- `fn`: `Function` the test to be ran, this is the function that will receive the test data object, **this will have to be an asynchronous function**.
- Optionally, you can provide a `timeout` (in milliseconds) for specifying how long to wait for each row before aborting. _Note: The default timeout is 5 seconds._

Example:

```js
test.concurrent.each`
a | b | expected
${1} | ${1} | ${2}
${1} | ${2} | ${3}
${2} | ${1} | ${3}
`('returns $expected when $a is added $b', ({a, b, expected}) => {
expect(a + b).toBe(expected);
});
```

### `test.concurrent.only.each(table)(name, fn)`

Also under the alias: `it.concurrent.only.each(table)(name, fn)`

Use `test.concurrent.only.each` if you want to only run specific tests with different test data concurrently.

`test.concurrent.only.each` is available with two APIs:

#### `test.concurrent.only.each(table)(name, fn)`

```js
test.concurrent.only.each([
[1, 1, 2],
[1, 2, 3],
[2, 1, 3],
])('.add(%i, %i)', async (a, b, expected) => {
expect(a + b).toBe(expected);
});

test('will not be ran', () => {
expect(1 / 0).toBe(Infinity);
});
```

#### `` test.only.each`table`(name, fn) ``

```js
test.concurrent.only.each`
a | b | expected
${1} | ${1} | ${2}
${1} | ${2} | ${3}
${2} | ${1} | ${3}
`('returns $expected when $a is added $b', async ({a, b, expected}) => {
expect(a + b).toBe(expected);
});

test('will not be ran', () => {
expect(1 / 0).toBe(Infinity);
});
```


### `test.concurrent.skip.each(table)(name, fn)`

Also under the alias: `it.concurrent.skip.each(table)(name, fn)`

Use `test.concurrent.skip.each` if you want to stop running a collection of asynchronous data driven tests.

`test.concurrent.skip.each` is available with two APIs:

#### `test.skip.each(table)(name, fn)`

```js
test.concurrent.skip.each([
[1, 1, 2],
[1, 2, 3],
[2, 1, 3],
])('.add(%i, %i)', async (a, b, expected) => {
expect(a + b).toBe(expected); // will not be ran
});

test('will be ran', () => {
expect(1 / 0).toBe(Infinity);
});
```

#### `` test.skip.each`table`(name, fn) ``

```js
test.concurrent.skip.each`
a | b | expected
${1} | ${1} | ${2}
${1} | ${2} | ${3}
${2} | ${1} | ${3}
`('returns $expected when $a is added $b', async ({a, b, expected}) => {
expect(a + b).toBe(expected); // will not be ran
});

test('will be ran', () => {
expect(1 / 0).toBe(Infinity);
});
```

### `test.each(table)(name, fn, timeout)`

Also under the alias: `it.each(table)(name, fn)` and `` it.each`table`(name, fn) ``
Expand Down
48 changes: 48 additions & 0 deletions packages/jest-each/README.md
Expand Up @@ -19,6 +19,12 @@ jest-each allows you to provide multiple arguments to your `test`/`describe` whi
- 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`
- `.test.concurrent`
- Also under the alias: `.it.concurrent`
- `.test.concurrent.only`
- Also under the alias: `.it.concurrent.only`
- `.test.concurrent.skip`
- Also under the alias: `.it.concurrent.skip`
- `.describe` to runs test suites with parameterised data
- `.describe.only` to only run the parameterised suite of tests
- Also under the aliases: `.fdescribe`
Expand Down Expand Up @@ -180,6 +186,48 @@ each([
});
```

#### `.test.concurrent(name, fn)`

Aliases: `.it.concurrent(name, fn)`

```js
each([
[1, 1, 2],
[1, 2, 3],
[2, 1, 3],
]).test.concurrent('returns the result of adding %d to %d', (a, b, expected) => {
expect(a + b).toBe(expected);
});
```

#### `.test.concurrent.only(name, fn)`

Aliases: `.it.concurrent.only(name, fn)`

```js
each([
[1, 1, 2],
[1, 2, 3],
[2, 1, 3],
]).test.concurrent.only('returns the result of adding %d to %d', (a, b, expected) => {
expect(a + b).toBe(expected);
});
```

#### `.test.concurrent.skip(name, fn)`

Aliases: `.it.concurrent.skip(name, fn)`

```js
each([
[1, 1, 2],
[1, 2, 3],
[2, 1, 3],
]).test.concurrent.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))`
Expand Down