-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
BeforeAll hook is executed as many workers exist (in case of -parallel option) #1153
Comments
Each child process runs in its own node process and is basically an instance of cucumber executing only a subset of the total scenarios. Thus the slaves don't have access to anything the others do so each needs to call the before all hooks. If the child processes were all running within the same node process, then I agree before all would only need to be called once. While on this subject, do you think the docs need to be updated to mention any part of this conversation: https://github.com/cucumber/cucumber-js/blob/master/docs/cli.md#parallel-experimental |
So with current implementation is not possible to achieve it...can be this as option as beforeAll hook is executed by first/fastest child process and then all processes wait for finishing beforeAll and then continue? This will force actual name "beforeAll", imho should not care if scenarios are executed parallely or not. Thanks. |
Do you have a use case where it executing on all slaves causes an issue? |
IMHO there are many, our case:
I still think that BeforeAll implies that is executed before executing scenarios, no matter if they are executed sequentially or parallelly. It would be really nice if it's possible to implement that way. Thanks. |
I have the same problem: I need to execute some API calls to prepare some data in hook that I want to reuse for all tests that I run in parallel. But with current implementation each node execute the same hook that I really need to avoid. |
It would be possible to have the master run the beforeAll but then nothing thats set up can be shared with any of the slaves as they currently run in another process. We could potentially do something like the following: the master executes beforeAll and the upon spawning the slaves passes it some user configured data that are made available in some way. Would that solve the issue? I'm not sure how we could do this in a way though that it works the same in multi-process vs single process. For a current workaround, you could do your own syncing where the beforeAll content only runs on one slave (use the cucumber env vars to see the slave number) and other slaves have some way of knowing when that work is done (outside of cucumber, ie cache data in some file) |
"Would that solve the issue?" Yes, that could be solution. It would be great to see it in next major version. |
Thinking on solution design for this, I don't want the interface to be parallel specific. Thoughts on something like this: A new interface that allows users to provide a function that returns data that is passed to world constructors. This can be a nice way to setup data you want available to all scenarios either in serial or parallel mode? import {setWorldSharedDataBuilder} from 'cucumber'
setWorldSharedDataBuilder(() =>. {
// can be an async function too
// return value will be passed to World constructors as "sharedData"
return {'my': 'data'};
}) In parallel mode this would only be executed on the master node and sent to each slave. Think this needs to be separate from BeforeAll / AfterAll in case you need to setup shared resources per slave (like webdrivers) |
@charlierudolph has this been implemented at all(perhaps on a different branch?). Specifically I would like to spin up a server in the BeforeAll that runs only on master, waits for all the children to return, then shuts down. I've tried specifying child 0 to spin up the server but there seems to be no guarantee as tests will still be running when the server shuts down. |
Hi @sethtomy, this has not been started to my knowledge |
Our use case is similar but slightly different, something like @charlierudolph 's I am using
AfterAll({ afterEachChild: false }, function () {
// This is executed once, after all child processes have finished.
}}
AfterAll({ afterEachChild: true }, function () {
// This is executed after EACH child process has finished. This is the current behaviour.
}} |
@charlierudolph Any thoughts on the above interface? We are close to needing this enough to either contribute a fix, hack/fork to fix for ourselves, or put a bounty out. We'd much prefer to contribute an acceptable pull request. |
For custom processing of results, I'd suggest that be done in a separate process instead of being a part of cucumber. Otherwise, I think your proposal of adding an option makes sense. I would prefer something more like the following though: import { AfterAll, HookParallelMode } from 'cucumber';
AfterAll(function () {
// In parallel mode, executed on master and each slave (for backwards compatibility for now, good to change the default down the line or require parallel mode to be set when executing with parallel option)
});
AfterAll({ parallelMode: HookParallelMode.MASTER_ONLY }, function () {
// In parallel mode, executed only on master
});
AfterAll({ parallelMode: HookParallelMode.SLAVE_ONLY }, function () {
// In parallel mode, executed only on each slave
});
AfterAll({ parallelMode: HookParallelMode.MASTER_AND_SLAVE }, function () {
// In parallel mode, executed on master and each slave
}); |
In light of recent events I'd like to propose we adopt a different terminology than master/slave. My suggestion: primary/secondary. It's all over the Internet, but here is a place to start: https://www.inputmag.com/culture/github-others-are-replacing-racist-terms-like-master-slave |
I quite like hive/worker for the parallel thing |
Many of the alternatives are also use socially charged terms. I think my favourite is
Or just |
@charlierudolph Would you mind elaborating a little more on this idea please?
|
I like coordinator / worker. I'll make updates to the code for that.
For this, I was thinking essentially something like a shell script that runs cucumber and after it exits, use another program for parsing the results (not totally sure what you are processing) |
The details we need in our results (URLs for Ghost Inspector result pages) are not available in Cucumber results objects. (Is the Cucumber result schema is extensible?) These URLs become available in the After step. The approach you describe would require storing the URLs somewhere in each after step, then reading it elsewhere once cucumber is complete. I was hoping to avoid that. But it is the workaround we have in mind if we can't easily contribute a solution to this in Cucumber JS. |
@charlierudolph Would you (in principle) accept a pull request that implements your proposal from a few comments ago, but with I.e. import { AfterAll, HookParallelMode } from 'cucumber';
AfterAll(function () {
// In parallel mode, executed on master and each slave (for backwards compatibility for now, good to change the default down the line or require parallel mode to be set when executing with parallel option)
});
AfterAll({ parallelMode: HookParallelMode.COORDINATOR_ONLY }, function () {
// In parallel mode, executed only on the coordinator, after all workers have completed.
});
AfterAll({ parallelMode: HookParallelMode.WORKER_ONLY }, function () {
// In parallel mode, executed only on each worker after it has completed.
});
AfterAll({ parallelMode: HookParallelMode.COORDINATOR_AND_WORKER }, function () {
// In parallel mode, executed on the coordinator after all workers have completed, and on each worker after it has completed.
});
// In serial mode, these are all equivalent; each callback is executed once all scenarios have completed. |
Yep |
Hi guys |
As far as I know, no. It seems no PR has been submitted yet :( |
Hi guys.
Do you have any updates? |
I'm using P.S. or even better to use |
Note, this is not guaranteed to work (ie other workers won't necessarily wait for it to finish if its a async process). |
Agreed, I also use pause for other workers while actions that are common to all workers are completed in BeforeAll hook. |
As a workaround, when the worker is 0 I create a specific file with a specific name (empty file). And when the worker is different than 0 I wait for that file to be created. In that way, the rest of the workers will have to wait until worker 0 finished creating that file. |
Hi @RockMinsk and @fescobar, How have you stopped or paused the other worker process while the first worker is run in BeforeAll hook? Thank you in advance. |
@enespekkaya In my case, the first worker process is responsible for creating the file. Meanwhile, other processes check if that file exists, if the file doesn't exist, that process keeps checking until the first worker creates that file. That verification is made every X seconds. |
Maybe take approach used in Playwright - keep |
Hi @fescobar , |
Hi @vitalets, |
My suggestion is to implement module.exports = {
default: {
globalSetup: ['features/globalSetup.ts'],
// ... PS: for using cucumber-js with playwright you may also have a look on playwright-bdd, that supports all playwright config options. |
Given this has turned into a discussion I'm going to propose we close this as "will not do". In essence Lots of the proposed workarounds are fine, and just that... "workarounds". In essence if you need something once and once only, you should explore some sort of preliminary step in your CI / other environment. Things like attaching to a specific parallel worker are ok in practice, but we would never evangelise / recommend these as these ID's are often assigned arbitrarily, and as such you might find that the first id numerically may not necessarily be the first one procedurally - YMMV. If you want to go down the route of waiting for that specific action to complete, again that's fine. |
Hi,
using Cucumber 5.0.2 with --parallel 3 option
in BeforeAll hook I have simple console.log() command.
Actual behavior
console.log() is executed so many as slave exists, in this case 3x
Expected behavior
BeforeAll hook should be executed only once, so console.log has just one output
or I am missing something?
The text was updated successfully, but these errors were encountered: