cucumber / cucumber-js

Cucumber for JavaScript
https://cucumber.io
MIT License
5.05k stars 1.09k forks source link

Parallel execution - Allow programmatic registration of per-scenario cleanup hooks #2156

Open jan-molak opened 2 years ago

jan-molak commented 2 years ago

🤔 What's the problem you're trying to solve?

To enable the cleanup of resources used during a test scenario (closing web browsers, disconnecting database connections, etc.), Serenity/JS programmatically registers an artificial After hook. This hook returns a Promise which gets resolved when the cleanup is done.

This approach relies on the fact that the Serenity/JS test formatter is initialised in the same Node process as the test scenarios to be executed, which means that the artificial After hook gets merged with user-defined step definitions.

However, when Cucumber.js is executed in parallel mode, Serenity/JS formatter gets initialised in a separate process, which means that Cucumber worker processes are not aware of the cleanup hook and can't execute it.

This makes it impossible for Serenity/JS users to use the framework with Cucumber.js parallel execution mode at the moment (serenity-js/serenity-js#1308).

Note that this problem doesn't affect Serenity/JS + Cucumber.js combo executed by an external parallel runner like WebdriverIO or Protractor, since those runners attach the reporter to the process executing the tests.

✨ What's your proposed solution?

I do like the idea of keeping the reporter process separate from the runner processes and using Cucumber messages to communicate between the worker and the coordinator processes. It makes the overall design cleaner and makes the Cucumber.js execution model similar to how Playwright Test works (which Serenity/JS already supports). I wouldn't change anything about that.

What I'd like to be able to do, though, is instruct Cucumber to perform the additional programmatically-defined clean-up step whenever the worker finishes the test scenario.

Ideally, I'd like to be able to do it from the existing Serenity/JS formatter, without having to write a whole new wrapper around Cucumber (just yet).

⛏ Have you considered any alternatives or workarounds?

My target solution will most likely be to write a Serenity/JS CLI wrapper around Cucumber, as per https://github.com/cucumber/cucumber-js/discussions/2041, assuming that the new programmatic Cucumber API will allow me to register custom After hooks.

Unfortunately, I don't have the capacity to explore that properly yet and would like to enable Serenity/JS community to benefit from Cucumber parallel execution mode before that's complete, if possible.

Alternatively, we could also introduce a concept of per-worker reporter in Cucumber, that's separate from the main reporter.

davidjgoss commented 1 year ago

Sorry for the late reply @jan-molak! Just wanted to clarify a couple of things:

jan-molak commented 1 year ago

Hey @davidjgoss - no problem at all, thanks for getting back to me!

The After hook needs a Serenity/JS "scene ID", which is a serialisable value initialised by Serenity/JS Cucumber reporter on the serenity instance when the reporter receives a Cucumber testCaseStarted message.

So to access this value, the hook could:

Thinking about it, scene ID is a serialisable value that's initialised in the process where the reporter is running. This is not ideal, but is currently the only place where I can do it.

What would be ideal, is if plugin developers could have a way to define per-worker Before and After hooks for Cucumber to attach to every worker, and then use the Cucumber message protocol to send custom data from such hooks to reporters.

This way I could:

Other than the scene ID, the per-worker Before and After hooks won't need any other data from the worker or the parent process. Metadata like the worker process id or some worker identifier could be a nice thing to attach to the report, but they're not necessary.

Of course, such programmatic Before hook would need to be executed before any user-defined Before hooks, and the programmatic After hook would need to be executed after any user-defined After hooks.