-
-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #683 from jakecollins/guice-scenario-scope
Guice module - add new scenario scope
- Loading branch information
Showing
56 changed files
with
1,452 additions
and
370 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Using Cucumber Guice | ||
|
||
The [package documentation](src/main/java/cucumber/api/guice/package.html) contains detailed instructions for using | ||
Cucumber Guice. In particular be sure to read the migration section if upgrading from earlier versions of Cucumber | ||
Guice. |
13 changes: 13 additions & 0 deletions
13
guice/src/main/java/cucumber/api/guice/CucumberModules.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package cucumber.api.guice; | ||
|
||
import com.google.inject.Module; | ||
import cucumber.runtime.java.guice.impl.ScenarioModule; | ||
|
||
/** | ||
* Provides a convenient <code>com.google.inject.Module</code> instance that contains bindings for | ||
* code>cucumber.runtime.java.guice.ScenarioScoped</code> annotation and for | ||
* <code>cucumber.runtime.java.guice.ScenarioScope</code>. | ||
*/ | ||
public class CucumberModules { | ||
public static final Module SCENARIO = new ScenarioModule(CucumberScopes.SCENARIO); | ||
} |
12 changes: 12 additions & 0 deletions
12
guice/src/main/java/cucumber/api/guice/CucumberScopes.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package cucumber.api.guice; | ||
|
||
import cucumber.runtime.java.guice.ScenarioScope; | ||
import cucumber.runtime.java.guice.impl.SequentialScenarioScope; | ||
|
||
/** | ||
* Provides a convenient <code>cucumber.runtime.java.guice.ScenarioScope</code> instance for use when declaring bindings | ||
* in implementations of <code>com.google.inject.Module</code>. | ||
*/ | ||
public class CucumberScopes { | ||
public static final ScenarioScope SCENARIO = new SequentialScenarioScope(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,8 @@ | ||
package cucumber.api.guice; | ||
|
||
|
||
/** | ||
* Please see documentation in package.html | ||
*/ | ||
public class README { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,141 @@ | ||
<body> | ||
<p> | ||
There is no API for this module, but by including the <code>cucumber-guice</code> jar | ||
on your <code>CLASSPATH</code> your Step Definitions will be instantiated by Guice. | ||
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.6 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.6 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<MyStatefulObject> providerMyStatefulObject; | ||
|
||
@Inject | ||
public MyStatelessSteps(Provider<MyStatefulObject> 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> |
100 changes: 0 additions & 100 deletions
100
guice/src/main/java/cucumber/runtime/java/guice/GuiceFactory.java
This file was deleted.
Oops, something went wrong.
8 changes: 0 additions & 8 deletions
8
guice/src/main/java/cucumber/runtime/java/guice/GuiceModuleInstantiationFailed.java
This file was deleted.
Oops, something went wrong.
12 changes: 12 additions & 0 deletions
12
guice/src/main/java/cucumber/runtime/java/guice/InjectorSource.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package cucumber.runtime.java.guice; | ||
|
||
import com.google.inject.Injector; | ||
|
||
/** | ||
* An implentation of this interface is used to obtain an <code>com.google.inject.Injector</code> that is used to | ||
* provide instances of all the classes that are used to run the Cucumber tests. The injector should be configured with | ||
* a binding for <code>cucumber.runtime.java.guice.ScenarioScope</code>. | ||
*/ | ||
public interface InjectorSource { | ||
Injector getInjector(); | ||
} |
8 changes: 0 additions & 8 deletions
8
guice/src/main/java/cucumber/runtime/java/guice/LoadingPropertiesFileFailed.java
This file was deleted.
Oops, something went wrong.
20 changes: 0 additions & 20 deletions
20
guice/src/main/java/cucumber/runtime/java/guice/ModuleInstantiator.java
This file was deleted.
Oops, something went wrong.
12 changes: 12 additions & 0 deletions
12
guice/src/main/java/cucumber/runtime/java/guice/ScenarioScope.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package cucumber.runtime.java.guice; | ||
|
||
import com.google.inject.Scope; | ||
|
||
/** | ||
* A custom Guice scope that enables classes to be bound in a scope that will last for the lifetime of one Cucumber | ||
* scenario. | ||
*/ | ||
public interface ScenarioScope extends Scope { | ||
void enterScope(); | ||
void exitScope(); | ||
} |
17 changes: 17 additions & 0 deletions
17
guice/src/main/java/cucumber/runtime/java/guice/ScenarioScoped.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package cucumber.runtime.java.guice; | ||
|
||
import com.google.inject.ScopeAnnotation; | ||
|
||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.Target; | ||
|
||
import static java.lang.annotation.ElementType.METHOD; | ||
import static java.lang.annotation.ElementType.TYPE; | ||
import static java.lang.annotation.RetentionPolicy.RUNTIME; | ||
|
||
/** | ||
* A custom Guice scope annotation that is usually bound to an instance of | ||
* <code>cucumber.runtime.java.guice.ScenarioScope</code>. | ||
*/ | ||
@Target({ TYPE, METHOD }) @Retention(RUNTIME) @ScopeAnnotation | ||
public @interface ScenarioScoped {} |
Oops, something went wrong.