Codeceptjs: Async/blocking BeforeSuite, Before, After, AfterSuite hooks

Created on 2 Mar 2017  ·  10Comments  ·  Source: codeceptjs/CodeceptJS

What are you trying to achieve?

I would like to asynchronously create a test user and a test project over an XHR request before a collection of tests runs.

I expected one of the following to work:

BeforeSuite((I) => new Promise((resolve) => setTimeout(resolve, 5000)))
Before((I) => new Promise((resolve) => setTimeout(resolve, 5000)))
BeforeSuite(function* (I) {
  yield new Promise((resolve) => setTimeout(resolve, 5000))
})
Before(function* (I) {
  yield new Promise((resolve) => setTimeout(resolve, 5000))
})

What do you get instead?

Instead, the tests progress without any blocking/waiting. Is there any approach I can take to postbone test execution until my prerequisits are fulfilled?

Details

  • CodeceptJS version: v0.5.1
  • NodeJS Version: v7.2
  • Operating System: MacOS Sierra
  • WebDriverIO version: v4.6.2
  • Configuration file:
exports.config = {
  tests: './tests/src/end-to-end/**/*.[tj]s',
  timeout: 10000,
  output: './tests/output/end-to-end',
  helpers: {
    WebDriverIO: {
      url: process.env.GUI_HOST,
      browser: 'chrome',
      restart: true,
      desiredCapabilities: {
        browserName: 'chrome',
      },
      host: 'selenium',
      port: 4444,
    },
    TestUser: {
      "require": "./tests/config/codecept/TestUser.js",
    },
    TestBusinessEntity: {
      "require": "./tests/config/codecept/TestBusinessEntity.js",
    },
  },
  include: {
    I: './tests/config/codecept/custom_steps.js',
  },
  bootstrap: false,
  mocha: {},
  name: 'byll-gui',
};
question

Most helpful comment

Ok, promises are stored in a global promise chain. There is a way to access it (I will post it below, however it is not documented and not recommended to do so). To attach a promise to a promise chain it should be implemented as a method inside a helper and called inside a test:

BeforeSuite((I) => {
   I.doSomethingBeforeStart();
});

doSomethingBeforeStart should be defined inside a custom helper as a regular method returning a promise:

doSomething() {
    new Promise((resolve) => setTimeout(resolve, 5000))
}

Once the helper is enabled you can use its method inside Before/BeforeSuite.


Alternatively you can access a promise chain on your own

```js
const recorder = require('codeceptjs/lib/recorder');

Before(function() {
recorder.add(new Promise((resolve) => setTimeout(resolve, 5000)));
});
```

All 10 comments

Ok, promises are stored in a global promise chain. There is a way to access it (I will post it below, however it is not documented and not recommended to do so). To attach a promise to a promise chain it should be implemented as a method inside a helper and called inside a test:

BeforeSuite((I) => {
   I.doSomethingBeforeStart();
});

doSomethingBeforeStart should be defined inside a custom helper as a regular method returning a promise:

doSomething() {
    new Promise((resolve) => setTimeout(resolve, 5000))
}

Once the helper is enabled you can use its method inside Before/BeforeSuite.


Alternatively you can access a promise chain on your own

```js
const recorder = require('codeceptjs/lib/recorder');

Before(function() {
recorder.add(new Promise((resolve) => setTimeout(resolve, 5000)));
});
```

I think it would be a good idea to implement handling of promises in Before/After in a next version. So probably its a good idea for a feature

Awesome, thanks for your quick response, @DavertMik!

I just looked into this again, and have 2 questions:


The second version (accessing the promise chain) does not work for me:

// example2.js
const recorder = require('codeceptjs/lib/recorder')

Feature('Example2')

Before(function (I) {
  recorder.add(new Promise((resolve) => setTimeout(() => {
    resolve()
    console.log('5 secs are over')
  }, 5000)))
  I.say('Before should have waited for 5 secs')
})

Scenario('Test scenario', function* (I) {
  I.say('Starting test')
  I.amOnPage(`/`)
})

Output:

> node_modules/.bin/codeceptjs run --steps . example2.js
CodeceptJS v0.5.1
Using test root "/Users/philipstanislaus/Code/basement-startup/byll-gui"

Example2 --
 Test scenario
   Before should have waited for 5 secs
   Starting test
 • I am on page "/"
 ✓ OK in 1199ms


  OK  | 1 passed   // 2s
5 secs are over

("5 secs are over" should have come first, instead the suite passes in 2s)

a) What is wrong here?


Your first method works as follows:

// Example.js
class Example extends Helper {
  fetchUser() {
    return new Promise((resolve) => setTimeout(() => {
      resolve({ name: 'Cleopatra' })
      console.log('5 secs are over')
    }, 5000))
  }
}

module.exports = Example


// example1.js
Feature('Example1')

Before(function (I) {
  I.fetchUser()
  I.say('Before should have waited for 5 secs')
})

Scenario('Test scenario', function* (I) {
  I.say(`Starting test`)
  I.amOnPage(`/`)
})

Output:

> node_modules/.bin/codeceptjs run --steps . example1.js                     
CodeceptJS v0.5.1
Using test root "/Users/philipstanislaus/Code/basement-startup/byll-gui"

Example1 --
 Test scenario
 • I fetch user 
5 secs are over
   Before should have waited for 5 secs
   Starting test
 • I am on page "/"
 ✓ OK in 993ms


  OK  | 1 passed   // 7s

Yet it does not seem to work with generator functions as tests do, is that expected?

Example:

// Example.js
// same as above...

// example1.js
Feature('Example1')

let user

Before(function* (I) {
  yield user = I.fetchUser()
  I.say('Before should have waited for 5 secs')
})

Scenario('Test scenario', function* (I) {
  I.say(`Starting test with user ${user}`)
  I.amOnPage(`/`)
})

Output:

> node_modules/.bin/codeceptjs run --steps . example1.js                     
CodeceptJS v0.5.1
Using test root "/Users/philipstanislaus/Code/basement-startup/byll-gui"

Example1 --
 Test scenario
   Starting test with user undefined
 • I am on page "/"
 ✓ OK in 1163ms


  OK  | 1 passed   // 2s

(Does not even output "Before should have waited for 5 secs" or "5 secs are over")

@philipstanislaus Hey, have you found a way yet to achieve asynchronous calls in before hooks?

@sveneisenschmidt unfortunately I have not, I used a test to achieve what I wanted. Let us know if you find a solution!

@DavertMik Hey ☺️, do you think this is something we can see as a feature in one of the upcoming releases?

I think it would be a good idea to implement handling of promises in Before/After in a next version. So probably its a good idea for a feature

Otherwise, I haven't had a look into the internal code yet, but I would be glad to help out as well. Maybe you can point me to a specific part of the code that needs to be extended to support promises and/or generators in Before/After hooks.

Any feedback is appreciated, have a nice day ☺️

@DavertMik @philipstanislaus My lovely co-workers found a solution. Will post an update tomorrow.

@philipstanislaus
node version: v8.1.3

Output:

> codeceptjs run --steps                                                                                                       │Only local connections are allowed.
                                                                                                                               │09:48:20.350 INFO - Detected dialect: OSS
CodeceptJS v0.6.3                                                                                                              │09:48:20.353 INFO - Done: [new session: Capabilities [{rotatable=true, locationContextEnabled=true, loggingPrefs=org.openqa.sel
Using test root "/home/seisenschmidt/Projects/workshop"                                                                        │enium.logging.LoggingPreferences@50746fb3, browserName=chrome, javascriptEnabled=true, chromeOptions={args=[]}, handlesAlerts=t
                                                                                                                               │rue, requestOrigins={name=webdriverio, version=4.6.2, url=http://webdriver.io}}]]
Title --                                                                                                                       │09:48:20.379 INFO - Executing: [script wait: 1000])
 test something                                                                                                                │09:48:20.379 INFO - Executing: [set window size])
 • I am on page "/100/de_DE/hotel/show?h[hotelId]=5001"                                                                        │09:48:20.381 INFO - Executing: [get: https://express.trivago.com/100/de_DE/hotel/show?h[hotelId]=5001])
 • I grab text from "body h1"                                                                                                  │09:48:20.385 INFO - Done: [script wait: 1000]
[ 'Best Western Hotel Goldenes Rad', '' ]                                                                                      │09:48:21.661 INFO - Done: [get: https://express.trivago.com/100/de_DE/hotel/show?h[hotelId]=5001]
 Interative debug session started                                                                                              │09:48:21.666 INFO - Executing: [get current url])
 Use JavaScript syntax to try steps in action                                                                                  │09:48:21.841 INFO - Done: [set window size]
 Press ENTER to continue                                                                                                       │09:48:21.864 INFO - Done: [get current url]
 I.                                                                                                                            │09:48:21.882 INFO - Executing: [find elements: By.cssSelector: body h1])
Exiting interactive shell....                                                                                                  │09:48:21.933 INFO - Done: [find elements: By.cssSelector: body h1]
 ✓ OK 

Scenario:

Feature('Title');

Before(async function(I, Page) {
    Page.LandingPage.open(100, 'de_DE', 5001);
    const text = await I.grabTextFrom('body h1');
    console.log(text);
});

Scenario('test something', function (I, Page) {
    pause();
});

async/await can be used in Before/After
Generators support will be in next releases https://github.com/Codeception/CodeceptJS/issues/206

Was this page helpful?
0 / 5 - 0 ratings

Related issues

medtoure18 picture medtoure18  ·  14Comments

waryhermit picture waryhermit  ·  12Comments

ericelliott picture ericelliott  ·  13Comments

vitaly87 picture vitaly87  ·  11Comments

gaurav21r picture gaurav21r  ·  10Comments