Skip to content
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

Add @BeforeAll and @AfterAll hooks #515

Closed
aslakhellesoy opened this issue May 2, 2013 · 128 comments · Fixed by #1876
Closed

Add @BeforeAll and @AfterAll hooks #515

aslakhellesoy opened this issue May 2, 2013 · 128 comments · Fixed by #1876
Labels
🥒 core team Candidate for going onto the Cucumber Open Board: https://github.com/orgs/cucumber/projects/8 🧷 pinned Tells Stalebot not to close this issue
Milestone

Comments

@aslakhellesoy
Copy link
Contributor

People keep asking for BeforeAll and AfterAll hooks. I haven't had a big need for them myself, but it seems to me this would be sufficient:

public class GlobalHooks {
    private static boolean dunit = false;

    @Before
    public void beforeAll() {
        if(!dunit) {
            Runtime.getRuntime().addShutdownHook(afterAllThread);
            // do the beforeAll stuff...
            dunit = true;
        }
    }
}

If this doesn't cut it for you, please explain in this ticket, and maybe we'll add special support for it.

@mattwynne
Copy link
Member

Does that fire when the process dies, or just the current cucumber run? What if you wanted to do multiple runs without killing the process? (In an Aruba 5.2 stylee)

@aslakhellesoy
Copy link
Contributor Author

It wouldn't be triggered by a VM shutdown. It would be part of the runner's life cycle and fire after the last scenario.

For Java I think they would have to be defined as static methods as well.

@tmertens
Copy link

tmertens commented May 2, 2013

What would happen if you try to embed data into the report in this
"BeforeAll" hook? It seems your example would work well for running some
code only once at the start of the scenarios, but any embedded would be
inserted into the first scenario that runs that code and not at the root
level of the report.

Furthermore, I supposed embed wouldn't work in the shutdown hook either in
the above case. Given scenario.embed is used for embedding data in
reports, I'm guessing there would have to be some new object reference in
order to embed data in the reports for @BeforeAll/@afterall hooks outside
of the scenarios.

Cheers,

-Tim

On Thu, May 2, 2013 at 6:46 AM, Aslak Hellesøy [email protected]:

It wouldn't be triggered by a VM shutdown. It would be part of the
runner's life cycle and fire after the last scenario.

For Java I think they would have to be defined as static methods as well.


Reply to this email directly or view it on GitHubhttps://github.com//issues/515#issuecomment-17333288
.

@aslakhellesoy
Copy link
Contributor Author

What would happen if you try to embed data into the report in this "BeforeAll" hook?

From a BeforeAll/AfterAll hook you would have optional access to a Report object with similar embed methods as the Scenario object available to Before/After hooks. Embeddings embedded from a BeforeAll would end up at the top of the report, before the first scenario. Similar for embeddings embedded from AfterAll - they would end up in the report after the last scenario.

If you attempt to do a Scenario.embed from an AfterAll hook you would get an exception - the scenario would be "closed" by then.

@mikquinlan
Copy link

We definitely have many uses for this. Currently we use a static
initialiser (which I think we got from this forum) to start up embedded
databases, Hadoop's Mini MR cluster among other things.

It would be good to have a clear annotation such as @BeforeAll and
@afterall rather than a specific method name with an @before tag.

On 2 May 2013 07:37, Aslak Hellesøy [email protected] wrote:

What would happen if you try to embed data into the report in this
"BeforeAll" hook?

From a BeforeAll/AfterAll hook you would have optional access to a Reportobject with similar embed methods as the
Scenario object available to Before/After hooks. Embeddings embedded from
a BeforeAll would end up at the top of the report, before the first
scenario. Similar for embeddings embedded from AfterAll - they would end
up in the report after the last scenario.

If you attempt to do a Scenario.embed from an AfterAll hook you would get
an exception - the scenario would be "closed" by then.


Reply to this email directly or view it on GitHubhttps://github.com//issues/515#issuecomment-17341593
.

@pranasblk
Copy link

+1. I want having multiple BeforeAll and AfterAll supported (opposite to not like World)

@mikquinlan
Copy link

Any movement on this issue? I'd love to have it. Of course, I'm just one person. ;-)

@aslakhellesoy
Copy link
Contributor Author

Those of you who need it - how about sending a pull request?

@quantoid
Copy link
Contributor

quantoid commented Jun 4, 2013

+1 because the trick above won't work for AfterAll

@paoloambrosio
Copy link
Member

@quantoid It's not a trick, it's Java! Why do you say that it does not work for AfterAll? The only Issue I see is with reports.

The @AfterAll hook would be the afterAllThread variable in Aslak's code.

Added this snippet to the Java Hello World Example:

    @Before
    public void beforeAll() {
        if(!dunit) {
            Runtime.getRuntime().addShutdownHook(new Thread() {
                public void run() {
                    System.out.println("snake!");
                }
            });
            System.out.println("badger");
            dunit = true;
        }
    }

After changing the runner to use the progress formatter, when I run mvn test, I get the desired behaviour:

badger

.........
Tests run: 12, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.466 sec
snake!

@quantoid
Copy link
Contributor

quantoid commented Jun 5, 2013

Sorry, I can see how it works in Java, I should've added "...in Groovy" but could be wrong about that too.

I'd want both 'all' hooks to be able to access variables that other glue can access, i.e. those in the Script scope.

@paoloambrosio
Copy link
Member

Same thing in the groovy-calculator example with BeforeAll and AfterAll, accessing the World object that contains shared variables.

class CustomWorld {

    def value = null
    def dunit = false

    // ...
}

Before() {
    if (!dunit) {
        dunit = true

        println "badger"
        value = "snake!" // sets the value in the World

        addShutdownHook {
            println value // reads the value from the World
        }
    }
}

Note that if you change the value variable in other steps, the shutdown hook is not going to print snake!, so the value is not determined when the closure is defined but when it is called (at the very end).

This change to a step will make the AfterAll closure print mushroom:

Then(~"the stored result should be (.*)") { double expected ->
    value = "mushroom"
    assert expected == result
}

Of course it works even if you define the value variable outside of the World object.

@quantoid
Copy link
Contributor

quantoid commented Jun 6, 2013

Awesome, thanks! Where does the addShutdownHook come from? There seem to be so many classes mixed in to the scope of these closures.

@paoloambrosio
Copy link
Member

@quantoid No magic there, it's part of the Groovy Object API

@quantoid
Copy link
Contributor

quantoid commented Jun 6, 2013

Hmm, IMHO would be better to tear stuff down as part of the feature execution block rather than when the JVM shuts down.

@TarekSaid
Copy link

Hi,

I'd like to create a test database then destroy it after all the tests are done. The static boolean works for the @before tag, creating my database only once.

But how would I drop the database only after all my tests are done?

@aslakhellesoy
Copy link
Contributor Author

@TarekSaid this is a bug tracker, not a support forum. Please ask on the cukes google group. See https://github.com/cucumber/cucumber-jvm/blob/master/CONTRIBUTING.md

@tschmal
Copy link

tschmal commented Aug 1, 2013

This will work OK for as completely global before/after, but it doesn't work if you want the behavior per feature file. For example, I may have 5 different feature files, each one having 10 different scenarios. Per feature, I may want a before/after. This workaround isn't per feature, but is per EVERYTHING. A per feature before/after would be nice.

For example, JBehave has a @Before/AfterStories. The code discussed in this thread would be equivalent to that, so this annotation isn't really necessary. It also has @Before/AfterScenario, which is equivalent to Cucumber's @Before/After. However, JBehave also has a @Before/AfterStory (singular) which isn't really doable with Cucumber-JVM (that I can tell). This would be very useful.

If you think about it, JUnit has it, too, with @Before/AfterClass.

Maybe I'm blind and there is a way to do it in Cucumber... but I can't see it.

@paoloambrosio
Copy link
Member

@tschmal before/after feature hooks are out of the scope of this issue. They were discussed several times on The Cukes Google Group, so you might search there. In a recent thread I explained how to deal with feature tags. If you still think you need it, please write on the list first and explain your problem.

@wjpowell
Copy link
Contributor

wjpowell commented Aug 3, 2013

You don't need to do this in cucumber. Use the @BeforeClass and @afterclass annotation from within the JUnit test used to run the cucumber tests. This has the benefit of running only for the features specified by the paths or tags options.

@RunWith(Cucumber.class)
@Cucumber.Options(format = {"html:target/cucumber-html-report", "json-pretty:target/cucumber-json-report.json"})
public class RunCukesTest {

    @BeforeClass
    public static void setup() {
        System.out.println("Ran the before");
    }

    @AfterClass
    public static void teardown() {
        System.out.println("Ran the after");
    }
}

@aslakhellesoy
Copy link
Contributor Author

@wjpowell there is nothing in the Cucumber runner that handles @BeforeClass/@afterclass. Have you actually tried this?

@wjpowell
Copy link
Contributor

wjpowell commented Aug 3, 2013

@aslakhellesoy I've tried it, and it works. The Cucumber runner calls super.run which handles the @BeforeClass and @afterclass annotated methods, as well as any @ClassRule defined.

@aslakhellesoy
Copy link
Contributor Author

Thanks for the clarification. I thought it might be something like that.

@andryutz10
Copy link
Contributor

Pull request created for this issue: #672
Just waiting for someone to take a look and approve it if its all good.

@alisterscott
Copy link

Would love to see this included in the latest version :)

@mattwynne
Copy link
Member

I just used the BeforeClass and AfterClass annotations today and they worked perfectly. I'm not sure why we need to add our own BeforeAll / AfterAll hooks.

@andryutz10
Copy link
Contributor

@mattwynne, I see a few reasons:

  1. @BeforeClass and @afterclass are not part of the cucumber and having them in the runner class as static has no benefit but has a few downsides (usually a DI container is used and some components are injected in stepdefs classes to be used for "setUp/initialization" - how do you get those in the runner class on static context?? plus polluting the runner class with test logic)
  2. when you want to select a feature in Intellij Idea and run it, the cli.Main class will be used and your methods from runner class with @BeforeClass and @afterclass will never run.
  3. I don't know how many of you had this problem but in the last two years I used cucumber a lot, and I can say that because of small things like the setUp/tearDown for cucumber tests, your code for tests will get messy and hard to maintain because of the static state. For an example cucumber test, yes you can use in the runner @BeforeClass and @afterclass just to say that works, but in real world projects is not the same, you'll encounter all kinds of issues and something like @BeforeAll and @afterall helps a lot. That's my opinion and I might be wrong, but that is way I created this PR.

@aslakhellesoy
Copy link
Contributor Author

@mattwynne Because those annotations are from JUnit and they only work when you use the JUnit Cucumber runner.

Many people use Cucumber-JVM with the command-line runner, without JUnit at all, and then this would not work.

@hemanthsridhar
Copy link

https://medium.com/@hemanthsridhar92/global-hooks-in-cucumber-jvm-afc1be13e487
Please follow this tutorial

@mpkorstanje mpkorstanje removed the Core label Sep 22, 2019
@mpkorstanje mpkorstanje added this to the 6.0.0 milestone Nov 26, 2019
@mpkorstanje mpkorstanje modified the milestones: 6.0.0, 5.x.x Jan 25, 2020
mpkorstanje added a commit that referenced this issue Jan 26, 2020
Work in progress to fix #515.

TODO:
 - [ ] How to deal with failure?
  - [ ] Test with JUnit4
  - [ ] Test with JUnit5
  - [ ] Test with TestNG
  - [ ] Test with CLI
  - [ ] Invoke around semantics?
 - [ ] How to report execution results?
  - [ ] TeamCity Plugin
  - [ ] Pretty formatter
  - [ ] Messages/Events
@mpkorstanje mpkorstanje modified the milestones: 5.x.x, v6.x.x Apr 17, 2020
@mpkorstanje mpkorstanje modified the milestones: v6.x.x, 7.0.0 Jun 23, 2020
@RayEltaib
Copy link

Can you please provide one for @afterall
I need it to close the driver after all the testing is done I don't want to keep closing the driver every time after each scenario it wastes a lot of time.
Right now I achieve that via having a feature specifically for that but that creates a problem because my @after method has to deal with the UI.

@haroon-sheikh
Copy link
Contributor

@fstarred
Copy link

Hi there,
I came to next solution, that does not require hooks or @BeforeClass/etc JUnit annotations, maybe it will be useful for someone. It is valid for Java8 + io.cucumber model and its reworked plugin approach:

1. Create new plugin class by implementing `cucumber.api.formatter.Formatter` interface:
package com.foo.bar;

import cucumber.api.event.EventHandler;
import cucumber.api.event.EventPublisher;
import cucumber.api.event.TestRunFinished;
import cucumber.api.event.TestRunStarted;
import cucumber.api.formatter.Formatter;

public class Initialization implements Formatter {

    private EventHandler<TestRunStarted> setup = event -> {
        // setup static method(-s) here  
    };

    private EventHandler<TestRunFinished> teardown = event -> {
        // tear down static method(-s) here  
    };

    @Override
    public void setEventPublisher(EventPublisher publisher) {
        publisher.registerHandlerFor(TestRunStarted.class, setup);
        publisher.registerHandlerFor(TestRunFinished.class, teardown);
    }
}
1. Apply this plugin to Cucumber main class, e.g. like in next example:
@RunWith(Cucumber.class)
@CucumberOptions(
        features = "src/test/resources/features/",
        glue = "com.foo.definitions",
        plugin = {"com.foo.bar.Initialization"})
public class CucumberRunner {
}

or via command line option like -Dcucumber.options="--plugin com.foo.bar.Initialization" etc.
P.S. In this way we are able to implement not only BeforeAll/AfterAll behavior, but also interceptors for very different Cucumber events (before/after features, scenarios, steps), full list here.

I've tried this idea and it seems very interesting but my biggest question is, why exactly does the handler only run at the end of the suite?

Nice job!
For the latest cucumber core, use ConcurrentEventListener instead of Formatter class

@aurelien-reeves aurelien-reeves added the 🥒 core team Candidate for going onto the Cucumber Open Board: https://github.com/orgs/cucumber/projects/8 label Feb 17, 2021
@ThanhTra1
Copy link

Junit BeforeClass and AfterClass annotations work fine if you are using a JUnit runner. No issues with the static state.. Works fine with Page object Model for me.

Could you help me ? What should i do about it? I am stuck it a week and i dont have any ideal for it

@mpkorstanje mpkorstanje removed the 🙏 help wanted Help wanted - not prioritized by core team label Apr 3, 2021
mpkorstanje added a commit that referenced this issue Apr 3, 2021
`BeforeAll` and `AfterAll` hooks are executed before all scenarios are executed and
after all scenarios have been executed. A hook is declared by annotating a method.
This methods must be static and do not take any arguments.

Hooks are global, all hooks declared in any step definition class will be 
executed. The order in which hooks are executed is not defined. An explicit
order can be provided by using the `order` property in the annotation.

```java
package io.cucumber.example;

import io.cucumber.java.AfterAll;
import io.cucumber.java.BeforeAll;

public class StepDefinitions {

    @BeforeAll
    public static void beforeAll() {
        // Runs before all scenarios
    }

    @afterall
    public static void afterAll() {
        // Runs after all scenarios
    }
}
```

Notes:

 1. When used in combination with Junit 5, Maven Surefire, and/or Failsafe use 
    version `3.0.0-M5` or later.
 2. When used in combination with Junit 5 and InteliJ IDEA failures in before
    all and after all hooks do not fail a test run.

Fixes: #515
@mattwynne mattwynne moved this to Implemented in Cucumber Open Jan 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🥒 core team Candidate for going onto the Cucumber Open Board: https://github.com/orgs/cucumber/projects/8 🧷 pinned Tells Stalebot not to close this issue
Projects
Status: Implemented
Development

Successfully merging a pull request may close this issue.