Skip to content

Commit c7626b9

Browse files
authored
Retry befores (#3580)
* retry for hooks initial * retry for before & beforeSuite * updated tests, refactored timeouts * fixed tests for BDD * fixed bdd test * a better hook for a tes * updated docs for timeouts and retries * fixed examples * simplified test
1 parent 2092cf7 commit c7626b9

31 files changed

+564
-116
lines changed

docs/advanced.md

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ You can use this options for build your own [plugins](https://codecept.io/hooks/
186186
});
187187
```
188188
189-
## Timeout <Badge text="Updated in 3.2" type="warning"/>
189+
## Timeout
190190
191191
Tests can get stuck due to various reasons such as network connection issues, crashed browser, etc.
192192
This can make tests process hang. To prevent these situations timeouts can be used. Timeouts can be set explicitly for flaky parts of code, or implicitly in a config.
@@ -236,38 +236,60 @@ A timeout for a group of tests can be set on Feature level via options.
236236
Feature('flaky tests', { timeout: 30 })
237237
```
238238
239-
### Sum Up
239+
### Timeout Confguration <Badge text="Updated in 3.4" type="warning"/>
240240
241-
Let's list all available timeout options.
241+
Timeout rules can be set globally via config.
242242
243-
Timeouts can be set globally in config:
243+
To set a timeout for all running tests provide a **number of seconds** to `timeout` config option:
244244
245-
```js
246-
// in codecept.confg.js:
247-
{ // ...
248-
timeout: 30, // limit all tests in all suites to 30 secs
249-
250-
plugins: {
251-
stepTimeout: {
252-
enabled: true,
253-
timeout: 10, // limit all steps except waiters to 10 secs
254-
}
255-
}
256-
}
257245
246+
```js
247+
// inside codecept.conf.js or codecept.conf.ts
248+
timeout: 30, // limit all tests in all suites to 30 secs
258249
```
259250
260-
or inside a test file:
251+
It is possible to tune this configuration for a different groups of tests passing options as array and using `grep` option to filter tests:
261252
262253
```js
263-
// limit all tests in this suite to 10 secs
264-
Feature('tests with timeout', { timeout: 10 });
254+
// inside codecept.conf.js or codecept.conf.ts
265255

266-
// limit this test to 20 secs
267-
Scenario('a test with timeout', { timeout: 20 }, ({ I }) => {
268-
// limit step to 5 seconds
269-
I.limitTime(5).click('Link');
270-
});
256+
timeout: [
257+
10, // default timeout is 10secs
258+
259+
// but increase timeout for slow tests
260+
{
261+
grep: '@slow',
262+
Feature: 50
263+
},
264+
]
265+
```
266+
267+
> ℹ️ `grep` value can be string or regexp
268+
269+
It is possible to set a timeout for Scenario or Feature:
270+
271+
```js
272+
// inside codecept.conf.js or codecept.conf.ts
273+
timeout: [
274+
275+
// timeout for Feature with @slow in title
276+
{
277+
grep: '@slow',
278+
Feature: 50
279+
},
280+
281+
// timeout for Scenario with 'flaky0' .. `flaky1` in title
282+
{
283+
// regexp can be passed to grep
284+
grep: /flaky[0-9]/,
285+
Scenario: 10
286+
},
287+
288+
// timeout for all suites
289+
{
290+
Feature: 20
291+
}
292+
]
271293
```
272294
273295
Global timeouts will be overridden by explicit timeouts of a test or steps.

docs/basics.md

Lines changed: 115 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ If you face that error please make sure that all async functions are called with
345345

346346
## Running Tests
347347

348-
To launch tests use the `run` command, and to execute tests in [multiple browsers](/advanced/#multiple-browsers-execution) or [multiple threads](/advanced/#parallel-execution) use the `run-multiple` command.
348+
To launch tests use the `run` command, and to execute tests in [multiple threads](/advanced/parallel) using `run-workers` command.
349349

350350
### Level of Detail
351351

@@ -394,7 +394,7 @@ It is recommended to [filter tests by tags](/advanced/#tags).
394394
395395
### Parallel Run
396396

397-
Since CodeceptJS 2.3, you can run tests in parallel by using NodeJS workers. This feature requires NodeJS >= 11.6. Use `run-workers` command with the number of workers (threads) to split tests.
397+
Tests can be executed in parallel mode by using [NodeJS workers](https://nodejs.org/api/worker_threads.html). Use `run-workers` command with the number of workers (threads) to split tests into different workers.
398398

399399
```
400400
npx codeceptjs run-workers 3
@@ -532,13 +532,55 @@ This can be configured in [screenshotOnFail Plugin](/plugins/#screenshotonfail)
532532

533533
To see how the test was executed, use [stepByStepReport Plugin](/plugins/#stepbystepreport). It saves a screenshot of each passed step and shows them in a nice slideshow.
534534

535+
## Before
536+
537+
Common preparation steps like opening a web page or logging in a user, can be placed in the `Before` or `Background` hooks:
538+
539+
```js
540+
Feature('CodeceptJS Demonstration');
541+
542+
Before(({ I }) => { // or Background
543+
I.amOnPage('/documentation');
544+
});
545+
546+
Scenario('test some forms', ({ I }) => {
547+
I.click('Create User');
548+
I.see('User is valid');
549+
I.dontSeeInCurrentUrl('/documentation');
550+
});
551+
552+
Scenario('test title', ({ I }) => {
553+
I.seeInTitle('Example application');
554+
});
555+
```
556+
557+
Same as `Before` you can use `After` to run teardown for each scenario.
558+
559+
## BeforeSuite
560+
561+
If you need to run complex a setup before all tests and have to teardown this afterwards, you can use the `BeforeSuite` and `AfterSuite` functions.
562+
`BeforeSuite` and `AfterSuite` have access to the `I` object, but `BeforeSuite/AfterSuite` don't have any access to the browser, because it's not running at this moment.
563+
You can use them to execute handlers that will setup your environment. `BeforeSuite/AfterSuite` will work only for the file it was declared in (so you can declare different setups for files)
564+
565+
```js
566+
BeforeSuite(({ I }) => {
567+
I.syncDown('testfolder');
568+
});
569+
570+
AfterSuite(({ I }) => {
571+
I.syncUp('testfolder');
572+
I.clearDir('testfolder');
573+
});
574+
```
575+
535576
## Retries
536577

537578
### Auto Retry
538579

539-
You can auto-retry a failed step by enabling [retryFailedStep Plugin](/plugins/#retryfailedstep).
580+
Each failed step is auto-retried by default via [retryFailedStep Plugin](/plugins/#retryfailedstep).
581+
If this is not expected, this plugin can be disabled in a config.
540582

541-
> **[retryFailedStep plugin](/plugins/#retryfailedstep) is enabled by default** for new setups
583+
> **[retryFailedStep plugin](/plugins/#retryfailedstep) is enabled by default** incide global configuration
542584
543585
### Retry Step
544586

@@ -608,69 +650,102 @@ Scenario('Really complex', { retries: 2 },({ I }) => {});
608650
This scenario will be restarted two times on a failure.
609651
Unlike retry step, there is no `when` condition supported for retries on a scenario level.
610652

653+
### Retry Before <Badge text="Since 3.4" type="warning"/>
654+
655+
To retry `Before`, `BeforeSuite`, `After`, `AfterSuite` hooks, add corresponding option to a `Feature`:
656+
657+
* `retryBefore`
658+
* `retryBeforeSuite`
659+
* `retryAfter`
660+
* `retryAfterSuite`
661+
662+
For instance, to retry Before hook 3 times:
663+
664+
```js
665+
Feature('this have a flaky Befure', { retryBefore: 3 })
666+
```
667+
668+
Multiple options of different values can be set at the same time
669+
611670
### Retry Feature
612671

613672
To set this option for all scenarios in a file, add `retry` to a feature:
614673

615674
```js
616675
Feature('Complex JS Stuff').retry(3);
676+
// or
677+
Feature('Complex JS Stuff', { retries: 3 })
617678
```
618679

619680
Every Scenario inside this feature will be rerun 3 times.
620681
You can make an exception for a specific scenario by passing the `retries` option to a Scenario.
621682

622-
### Retry Run
623-
624-
On the highest level of the "retry pyramid" there is an option to retry a complete run multiple times.
625-
Even this is the slowest option of all, it can be helpful to detect flaky tests.
683+
### Retry Configuration <Badge text="Since 3.4" type="warning"/>
626684

627-
[`run-rerun`](https://codecept.io/commands/#run-rerun) command will restart the run multiple times to values you provide. You can set minimal and maximal number of restarts in configuration file.
685+
It is possible to set retry rules globally via `retry` config option. The configuration is flexible and allows multiple formats.
686+
The simplest config would be:
628687

688+
```js
689+
// inside codecept.conf.js
690+
retry: 3
629691
```
630-
npx codeceptjs run-rerun
631-
```
632-
633692

634-
## Before
693+
This will enable Feature Retry for all executed feature, retrying failing tests 3 times.
635694

636-
Common preparation steps like opening a web page or logging in a user, can be placed in the `Before` or `Background` hooks:
695+
An object can be used to tune retries of a Before/After hook, Scenario or Feature
637696

638697
```js
639-
Feature('CodeceptJS Demonstration');
698+
// inside codecept.conf.js
699+
retry: {
700+
Feature: ...,
701+
Scenario: ...,
702+
Before: ...,
703+
BeforeSuite: ...,
704+
After: ...,
705+
AfterSuite: ...,
706+
}
707+
```
640708

641-
Before(({ I }) => { // or Background
642-
I.amOnPage('/documentation');
643-
});
709+
Multiple retry configs can be added via array. To use different retry configs for different subset of tests use `grep` option inside a retry config:
710+
711+
```js
712+
// inside codecept.conf.js
713+
retry: [
714+
{
715+
// enable this config only for flaky tests
716+
grep: '@flaky',
717+
Before: 3
718+
Scenario: 3
719+
},
720+
{
721+
// retry less when running slow tests
722+
grep: '@slow'
723+
Scenario: 1
724+
Before: 1
725+
}, {
726+
// retry all BeforeSuite
727+
BeforeSuite: 3
728+
}
729+
]
730+
```
644731

645-
Scenario('test some forms', ({ I }) => {
646-
I.click('Create User');
647-
I.see('User is valid');
648-
I.dontSeeInCurrentUrl('/documentation');
649-
});
732+
When using `grep` with `Before`, `After`, `BeforeSuite`, `AfterSuite`, a suite title will be checked for included value.
650733

651-
Scenario('test title', ({ I }) => {
652-
I.seeInTitle('Example application');
653-
});
654-
```
734+
> ℹ️ `grep` value can be string or regexp
655735
656-
Same as `Before` you can use `After` to run teardown for each scenario.
736+
Rules are applied in the order of array element, so the last option will override a previous one. Global retries config can be overridden in a file as described previously.
657737

658-
## BeforeSuite
738+
### Retry Run
659739

660-
If you need to run complex a setup before all tests and have to teardown this afterwards, you can use the `BeforeSuite` and `AfterSuite` functions.
661-
`BeforeSuite` and `AfterSuite` have access to the `I` object, but `BeforeSuite/AfterSuite` don't have any access to the browser, because it's not running at this moment.
662-
You can use them to execute handlers that will setup your environment. `BeforeSuite/AfterSuite` will work only for the file it was declared in (so you can declare different setups for files)
740+
On the highest level of the "retry pyramid" there is an option to retry a complete run multiple times.
741+
Even this is the slowest option of all, it can be helpful to detect flaky tests.
663742

664-
```js
665-
BeforeSuite(({ I }) => {
666-
I.syncDown('testfolder');
667-
});
743+
[`run-rerun`](https://codecept.io/commands/#run-rerun) command will restart the run multiple times to values you provide. You can set minimal and maximal number of restarts in configuration file.
668744

669-
AfterSuite(({ I }) => {
670-
I.syncUp('testfolder');
671-
I.clearDir('testfolder');
672-
});
673745
```
746+
npx codeceptjs run-rerun
747+
```
748+
674749

675750
[Here are some ideas](https://github.com/codeceptjs/CodeceptJS/pull/231#issuecomment-249554933) on where to use BeforeSuite hooks.
676751

examples/checkout_test.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
Feature('Checkout');
1+
Feature('Checkout', { retryBefore: 3 });
2+
3+
const i = 0;
24

35
Before(({ I }) => {
46
I.amOnPage('https://getbootstrap.com/docs/4.0/examples/checkout/');
7+
I.failNTimes(3);
58
});
69

710
Scenario('It should fill in checkout page', async ({ I }) => {
811
I.fillField('#lastName', 'mik');
9-
await eachElement('tick all checkboxes', '.custom-checkbox label', async (el) => {
10-
await el.click();
11-
});
12+
// await eachElement('tick all checkboxes', '.custom-checkbox label', async (el) => {
13+
// await el.click();
14+
// });
1215
await retryTo((retryNum) => {
1316
I.fillField('Promo code', '123345');
1417
I.click('Redeem');

examples/codecept.config.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
exports.config = {
22
output: './output',
3+
retry: 3,
34
helpers: {
45
Playwright: {
56
url: 'http://localhost',
@@ -14,6 +15,9 @@ exports.config = {
1415
show: !process.env.HEADLESS,
1516
},
1617
REST: {},
18+
User: {
19+
require: './user_helper.js',
20+
},
1721
},
1822
include: {
1923
I: './custom_steps.js',
@@ -60,7 +64,7 @@ exports.config = {
6064
},
6165
},
6266
tests: './*_test.js',
63-
timeout: 100,
67+
// timeout: 100,
6468
multiple: {
6569
parallel: {
6670
chunks: 2,

examples/user_helper.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
const assert = require('assert');
2-
const Helper = require('../lib/helper');
32

43
class User extends Helper {
5-
// before/after hooks
4+
_beforeSuite() {
5+
}
6+
67
_before() {
7-
// remove if not used
8+
this.i = 0;
89
}
910

10-
_after() {
11-
// remove if not used
11+
failNTimes(n) {
12+
this.i++;
13+
// this.i++;
14+
console.log(this.i, n);
15+
if (this.i < n) throw new Error('ups, error');
1216
}
1317

1418
// add custom methods here

lib/codecept.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ class Codecept {
9898
runHook(require('./listener/artifacts'));
9999
runHook(require('./listener/config'));
100100
runHook(require('./listener/helpers'));
101+
runHook(require('./listener/retry'));
101102
runHook(require('./listener/timeout'));
102103
runHook(require('./listener/exit'));
103104

lib/interfaces/featureConfig.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class FeatureConfig {
1919
* Set timeout for this suite
2020
* @param {number} timeout
2121
* @returns {this}
22+
* @deprecated
2223
*/
2324
timeout(timeout) {
2425
console.log(`Feature('${this.suite.title}').timeout(${timeout}) is deprecated!`);

0 commit comments

Comments
 (0)