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

Guice module - add new scenario scope #683

Merged
merged 2 commits into from
May 20, 2014

Conversation

techabstraction
Copy link

This commit adds a new Cucumber scenario scope to the Guice scopes available for use in your test code.

I realise this may be considered a controversial pull request as it's a complete rewrite of the module, rather than an incremental change. It's also not backwards compatible with the previous version of the module due to the new lifecycle of the Guice injector, see documentation below for details.

I'd be interested to hear whether the maintainers would consider rolling this out, and if so how it could fit into the roadmap given it's a breaking change?

From the new documentation in package.html:

<body>
<p>
    This module allows you to use Google Guice dependency injection in your Cucumber tests. Guice comes as standard with
    singleton scope and 'no scope'. This module adds Cucumber scenario scope to the scopes available for use in your
    test code. The rest of this documentation assumes you have at least a basic understanding of Guice. Please refer to
    the Google documentation if necessary, see https://code.google.com/p/google-guice/wiki/Motivation?tm=6

    <b>About scopes, injectors and migration from earlier versions</b>

    It's important to realise the differences in how this module functions when compared with earlier versions. The
    changes are as follows.

    <= 1.1.5    A Guice injector is created at the start of each test scenario and is destroyed at the end of each test
                scenario. There is no scenario scope, just singleton and 'no scope'.

    > 1.1.5     A Guice injector is created once before any tests are run and is destroyed after the last test has run.
                Before each test scenario a new scenario scope is created. At the end of the test scenario the scenario
                scope is destroyed. Singleton scope exists throughout all test scenarios.

    Users wishing to migrate to this version should replace <code>@Singleton</code> annotations with
    <code>@ScenarioScope</code> annotations. Guice modules should also have their singleton bindings updated. All
    bindings in <code>Scopes.SINGLETON</code> should be replaced with bindings in <code>CucumberScopes.SCENARIO</code>.

    <b>Using the module</b>

    By including the <code>cucumber-guice</code> jar on your <code>CLASSPATH</code> your Step Definitions will be
    instantiated by Guice. There are two main modes of using the module: with scope annotations and with module
    bindings. The two modes can also be mixed. When mixing modes it is important to realise that binding a class in a
    scope in a module takes precedence if the same class is also bound using a scope annotation.

    <b>Scoping your step definitions</b>

    Usually you will want to bind your step definition classes in either scenario scope or in singleton scope. It is not
    recommended to leave your step definition classes with no scope as it means that Cucumber will instantiate a new
    instance of the class for each step within a scenario that uses that step definition.

    <b>Scenario scope</b>

    Cucumber will create exactly one instance of a class bound in scenario scope for each scenario in which it is used.
    You should use scenario scope when you want to store state during a scenario but do not want the state to interfere
    with subsequent scenarios.

    <b>Singleton scope</b>

    Cucumber will create just one instance of a class bound in singleton scope that will last for the lifetime of all
    test scenarios in the test run. You should use singleton scope if your classes are stateless. You can also use
    singleton scope when your classes contain state but with caution. You should be absolutely sure that a state change
    in one scenario could not possibly influence the success or failure of a subsequent scenario. As an example of when
    you might use a singleton, imagine you have an http client that is expensive to create. By holding a reference to
    the client in a class bound in singleton scope you can reuse the client in multiple scenarios.

    <b>Using scope annotations</b>

    This is the easy route if you're new to Guice. To bind a class in scenario scope add the
    <code>cucumber.runtime.java.guice.ScenarioScoped</code> annotation to the class definition. The class should have
    a no-args constructor or one constructor that is annotated with <code>javax.inject.Inject</code>. For example:

    <code>
        import cucumber.runtime.java.guice.ScenarioScoped;
        import javax.inject.Inject;

        @ScenarioScoped
        public class ScenarioScopedSteps {

            private final Object someInjectedDependency;

            @Inject
            public ScenarioScopedSteps(Object someInjectedDependency) {
                this.someInjectedDependency = someInjectedDependency;
            }

            ...
        }
    </code>

    To bind a class in singleton scope add the <code>javax.inject.Singleton</code> annotation to the class definition.
    One strategy for using stateless step definitions is to use providers to share stateful scenario scoped instances
    between stateless singleton step definition instances. For example:

    <code>
        import javax.inject.Inject;
        import javax.inject.Singleton;

        @Singleton
        public class MyStatelessSteps {

            private final Provider&lt;MyStatefulObject&gt; providerMyStatefulObject;

            @Inject
            public MyStatelessSteps(Provider&lt;MyStatefulObject&gt; providerMyStatefulObject) {
                this.providerMyStatefulObject = providerMyStatefulObject;
            }

            @Given("^I have (\\d+) cukes in my belly$")
            public void I_have_cukes_in_my_belly(int n) {
                providerMyStatefulObject.get().iHaveCukesInMyBelly(n);
            }

            ...
        }
    </code>

    See https://code.google.com/p/google-guice/wiki/InjectingProviders "Providers for Mixing Scopes"

    <b>Using module bindings</b>

    As an alternative to using annotations you may prefer to declare Guice bindings in a class that implements
    <code>com.google.inject.Module</code>. To do this you should create a class that implements
    <code>cucumber.runtime.java.guice.InjectorSource</code>. This gives you complete control over how you obtain a
    Guice injector and it's Guice modules. The injector must provide a binding for
    <code>cucumber.runtime.java.guice.ScenarioScope</code>. It should also provide a binding for the
    <code>cucumber.runtime.java.guice.ScenarioScoped</code> annotation if your classes are using the annotation. The
    easiest way to do this it to use <code>CucumberModules.SCENARIO</code>. For example:

    <code>
        import com.google.inject.Guice;
        import com.google.inject.Injector;
        import com.google.inject.Stage;
        import cucumber.api.guice.CucumberModules;
        import cucumber.runtime.java.guice.InjectorSource;

        public class YourInjectorSource implements InjectorSource {

            @Override
            public Injector getInjector() {
                return Guice.createInjector(Stage.PRODUCTION, CucumberModules.SCENARIO, new YourModule());
            }
        }
    </code>

    Cucumber needs to know where to find the <code>cucumber.runtime.java.guice.InjectorSource</code> that it will use.
    You should create a properties file called <code>cucumber-guice.properties</code> and place it in the root of the
    classpath. The file should contain a single property key called <code>guice.injector-source</code> with a value
    equal to the fully qualified name of the <code>cucumber.runtime.java.guice.InjectorSource</code>. For example:

    <code>
        guice.injector-source=com.company.YourInjectorSource
    </code>

</p>
</body>

@aslakhellesoy
Copy link
Contributor

I posted a message to the list.

Let's see if someone has any comments. If I don't hear anything for a week we can merge this in.

aslakhellesoy added a commit that referenced this pull request Mar 23, 2014
@techabstraction
Copy link
Author

I just returned from a couple of weeks holiday. Is this now going to be merged?

@aslakhellesoy
Copy link
Contributor

This is already merged and part of the 1.1.6 release:

https://github.com/cucumber/cucumber-jvm/blob/master/History.md

Just forgot to close this.

@techabstraction
Copy link
Author

Could you please check again? I just pulled the latest code and I don't see the changes. Github is also telling me there are unmerged commits.

@brasmusson
Copy link
Contributor

@JakeCollins I'd say you are right, this PR is not merged. Only the attribution in History.md got into 1.1.6 (the attribution commit follows directly after the merge of another PR).

@brasmusson brasmusson reopened this Mar 31, 2014
@aslakhellesoy
Copy link
Contributor

@JakeCollins, @brasmusson is right. I intended to merge this, but I realise I never did.

Can you rebase your branch with master so we can give it another try?

Thanks,
Aslak

@techabstraction
Copy link
Author

@aslakhellesoy it's now rebased, please try again?

@techabstraction
Copy link
Author

@aslakhellesoy @brasmusson I guess you guys are busy, but just to remind you the guice-scenario-scope branch is now rebased ready for you to merge.

@techabstraction
Copy link
Author

@aslakhellesoy @brasmusson could you please give an update on this? Eg, will it be merged at some point? If not, no problem, but please let me know, then I can consider releasing it as a stand alone project.

@dkowis
Copy link
Member

dkowis commented May 20, 2014

I can push the merge button.

I'm going to go on the previous comments that nobody objected to it, and the rebase shows that it's good, and the travis build passed, so given the history I'm going to merge it.

As well, it's two commits if we need to unmerge it :)

dkowis added a commit that referenced this pull request May 20, 2014
Guice module - add new scenario scope
@dkowis dkowis merged commit a3acdbe into cucumber:master May 20, 2014
@aslakhellesoy
Copy link
Contributor

@dkowis please update History.md

@dkowis
Copy link
Member

dkowis commented May 20, 2014

@aslakhellesoy yep, I'm typing it up right now :)

@dkowis
Copy link
Member

dkowis commented May 20, 2014

@aslakhellesoy also, this is non-backwards compatible with the guice module, should it be at least 1.2? I don't remember if we'd update to 2.0 for this since it doesn't break the core API itself.

@techabstraction
Copy link
Author

@dkowis great news, thank you.

When you decide what the release version number should be, could you please put it on line 13 and 16 of this file:

https://github.com/cucumber/cucumber-jvm/blob/master/guice/src/main/java/cucumber/api/guice/package.html

(The file currently shows 1.1.6 which is now incorrect)

@aslakhellesoy
Copy link
Contributor

Please remove version numbers in all files. We'll never remember to update them in the future.

@aslakhellesoy
Copy link
Contributor

Ah never mind. I see it doesn't need to be updated in the future.

@techabstraction
Copy link
Author

@aslakhellesoy - Exactly, it's just telling people they need to migrate when they update to that version.

So it still remains to decide, what should be the release version number?

@brasmusson
Copy link
Contributor

@JakeCollins It is actually not necessary to know the next release version number. "> 1.1.7" is correct regardless of the next version is 1.1.8 or 1.2.0. I have updated the number.

@dkowis
Copy link
Member

dkowis commented May 21, 2014

@brasmusson good point! Thanks!

@techabstraction
Copy link
Author

@brasmusson lol! Yes, that was my thinking when I put '> 1.1.6' and 1.1.6 was the current released version. I forgot that when I made my earlier comment.

I guess as a nicety, but no big deal, we could update line 16 only when the release version is known, eg '>= X.X.X ...'

@techabstraction techabstraction deleted the guice-scenario-scope branch June 3, 2014 10:52
@lock
Copy link

lock bot commented Oct 25, 2018

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Oct 25, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants