Retry Flaky Tests
You can rerun certain tests with the WebdriverIO testrunner that turn out to be unstable due to things like a flaky network or race conditions. (However, it is not recommended to simply increase the rerun rate if tests become unstable!)
Rerun suites in Mocha
Since version 3 of Mocha, you can rerun whole test suites (everything inside an describe
block). If you use Mocha you should favor this retry mechanism instead of the WebdriverIO implementation that only allows you to rerun certain test blocks (everything within an it
block). In order to use the this.retries()
method, the suite block describe
must use an unbound function function(){}
instead of a fat arrow function () => {}
, as described in Mocha docs. Using Mocha you can also set a retry count for all specs using mochaOpts.retries
in your wdio.conf.js
.
Here is an example:
describe('retries', function () {
// Retry all tests in this suite up to 4 times
this.retries(4)
beforeEach(async () => {
await browser.url('http://www.yahoo.com')
})
it('should succeed on the 3rd try', async function () {
// Specify this test to only retry up to 2 times
this.retries(2)
console.log('run')
await expect($('.foo')).toBeDisplayed()
})
})
Rerun single tests in Jasmine or Mocha
To rerun a certain test block you can just apply the number of reruns as last parameter after the test block function:
- Mocha
- Jasmine
describe('my flaky app', () => {
/**
* spec that runs max 4 times (1 actual run + 3 reruns)
*/
it('should rerun a test at least 3 times', async function () {
console.log(this.wdioRetries) // returns number of retries
// ...
}, 3)
})
The same works for hooks too:
describe('my flaky app', () => {
/**
* hook that runs max 2 times (1 actual run + 1 rerun)
*/
beforeEach(async () => {
// ...
}, 1)
// ...
})
describe('my flaky app', () => {
/**
* spec that runs max 4 times (1 actual run + 3 reruns)
*/
it('should rerun a test at least 3 times', async function () {
console.log(this.wdioRetries) // returns number of retries
// ...
}, jasmine.DEFAULT_TIMEOUT_INTERVAL, 3)
})
The same works for hooks too:
describe('my flaky app', () => {
/**
* hook that runs max 2 times (1 actual run + 1 rerun)
*/
beforeEach(async () => {
// ...
}, jasmine.DEFAULT_TIMEOUT_INTERVAL, 1)
// ...
})
If you are using Jasmine, the second parameter is reserved for timeout. To apply a retry parameter you need to set the timeout to its default value jasmine.DEFAULT_TIMEOUT_INTERVAL
and then apply your retry count.
This retry mechanism only allows to retry single hooks or test blocks. If your test is accompanied with a hook to set up your application, this hook is not being run. Mocha offers native test retries that provide this behavior while Jasmine doesn't. You can access the number of executed retries in the afterTest
hook.