1
1
Cucumber Spring
2
2
===============
3
3
4
- Use Cucumber Spring to manage state between steps and for scenarios.
4
+ Use Cucumber Spring to share state between steps in a scenario and access the
5
+ spring application context.
5
6
6
7
Add the ` cucumber-spring ` dependency to your ` pom.xml ` :
7
8
@@ -18,136 +19,141 @@ Add the `cucumber-spring` dependency to your `pom.xml`:
18
19
</dependencies >
19
20
```
20
21
21
- ## Annotation Based Configuration
22
+ ## Configuring the Test Application Context
22
23
23
- For your own classes:
24
+ To make Cucumber aware of your test configuration you can annotate a
25
+ configuration class with ` @CucumberContextConfiguration ` and with one of the
26
+ following annotations: ` @ContextConfiguration ` , ` @ContextHierarchy ` or
27
+ ` @BootstrapWith ` . If you are using SpringBoot, you can annotate configuration
28
+ class with ` @SpringBootTest ` .
24
29
25
- * Add a ` @Component ` annotation to each of the classes ` cucumber-spring ` should
26
- manage.
30
+ For example:
27
31
``` java
28
- package com.example.app ;
32
+ import com.example.app ;
29
33
30
- import org.springframework.stereotype.Component ;
34
+ import org.springframework.boot.test.context.SpringBootTest ;
31
35
32
- @Component
33
- public class Belly {
34
- private int cukes = 0 ;
36
+ import io.cucumber.spring.CucumberContextConfiguration ;
35
37
36
- public void setCukes ( int cukes ) {
37
- this . cukes = cukes;
38
- }
38
+ @CucumberContextConfiguration
39
+ @SpringBootTest ( classes = TestConfig . class)
40
+ public class CucumberSpringConfiguration {
39
41
40
- public int getCukes () {
41
- return cukes;
42
- }
43
42
}
44
43
```
45
- * Add the location of your classes to the ` @ComponentScan ` of your (test)
46
- configuration:
44
+
45
+ Note: Cucumber Spring uses Springs ` TestContextManager ` framework internally.
46
+ As a result a single Cucumber scenario will mostly behave like a JUnit test.
47
+
48
+ For more information configuring Spring tests see:
49
+ - [ Spring Framework Documentation - Testing] ( https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/testing.html )
50
+ - [ Spring Boot Features - Testing] ( https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-testing )
51
+
52
+ ## Accessing the application context
53
+
54
+ Components from the application context can be accessed by autowiring.
55
+ Annotate a field in your step definition class with ` @Autowired ` .
47
56
48
57
``` java
49
58
package com.example.app ;
50
59
51
- import org.springframework.context.annotation.ComponentScan ;
52
- import org.springframework.context.annotation.Configuration ;
60
+ public class MyStepDefinitions {
61
+
62
+ @Autowired
63
+ private MyService myService;
53
64
54
- @Configuration
55
- @ComponentScan ( " com.example.app " )
56
- public class Config {
57
- // the rest of your configuration
65
+ @Given ( " feed back is requested from my service " )
66
+ public void feed_back_is_requested (){
67
+ myService . requestFeedBack();
68
+ }
58
69
}
59
70
```
60
71
61
- For classes from other frameworks:
72
+ ## Sharing State
62
73
63
- * You will have to explicitly register them as Beans in your (test) configuration:
74
+ Cucumber Spring creates an application context and using Springs
75
+ ` TestContextManager ` framework internally. All scenarios as well as all other
76
+ tests (e.g. JUnit) that use the same context configuration will share one
77
+ instance of the Spring application. This avoids an expensive startup time.
78
+
79
+ ### Sharing state between steps
80
+
81
+ To prevent sharing test state between scenarios, beans containing glue code
82
+ (i.e. step definitions, hooks, ect) are bound to the ` cucumber-glue ` scope.
83
+
84
+ The ` cucumber-glue ` scope starts prior to a scenario and ends after a scenario.
85
+ All beans in this scope will be created before a scenario execution and
86
+ disposed at the end of it.
87
+
88
+ By using the ` CucumberTestContext.SCOPE_CUCUMBER_GLUE ` additional components
89
+ can be added to the glue scope. These components can be used to safely share
90
+ state between scenarios.
64
91
65
92
``` java
66
93
package com.example.app ;
67
94
68
- import com.example.other.framework.SomeOtherService ;
69
- import org.springframework.context.annotation.Bean ;
70
- import org.springframework.context.annotation.ComponentScan ;
71
- import org.springframework.context.annotation.Configuration ;
95
+ import org.springframework.stereotype.Component ;
72
96
import org.springframework.context.annotation.Scope ;
97
+ import static io.cucumber.spring.CucumberTestContext ;
73
98
74
- @Configuration
75
- @ComponentScan (" com.example.app" )
76
- public class TestConfig {
77
- @Bean
78
- public SomeOtherService someOtherService () {
79
- // return an instance of some other service
80
- }
81
- }
82
- ```
83
-
84
- To make Cucumber aware of your test configuration you can annotate a
85
- configuration class with ` @CucumberContextConfiguration ` and with one of the
86
- following annotations: ` @ContextConfiguration ` , ` @ContextHierarchy ` or
87
- ` @BootstrapWith ` . If you are using SpringBoot, you can annotate configuration
88
- class with ` @SpringBootTest(classes = TestConfig.class) ` .
99
+ @Component
100
+ @Scope (CucumberTestContext . SCOPE_CUCUMBER_GLUE )
101
+ public class TestUserInformation {
89
102
90
- For example:
91
- ``` java
92
- import com.example.app ;
93
- import org.springframework.boot.test.context.SpringBootTest ;
94
- import io.cucumber.spring.CucumberContextConfiguration ;
103
+ private User testUser;
95
104
96
- @SpringBootTest ( classes = TestConfig . class)
97
- @CucumberContextConfiguration
98
- public class SomeServiceStepDefinitions {
105
+ public void setTestUser ( User testUser ) {
106
+ this . testUser = testUser;
107
+ }
99
108
109
+ public User getTestUser () {
110
+ return testUser;
111
+ }
100
112
101
113
}
102
114
```
103
115
104
- Now you can use the registered beans by autowiring them where you need them.
116
+ The glue scoped component can then be autowired into a step definition:
105
117
106
- For example:
107
118
``` java
108
- import com.example.app ;
109
- import org.springframework.beans.factory.annotation.Autowired ;
110
-
111
- public class SomeServiceStepDefinitions {
112
-
113
- @Autowired
114
- SomeService someService;
115
-
116
- @Autowired
117
- SomeOtherService someOtherService;
119
+ package com.example.app ;
118
120
119
- // the rest of your step definitions
120
- }
121
- ```
121
+ public class UserStepDefinitions {
122
122
123
- ### The Application Context & Cucumber Glue Scope
123
+ @Autowired
124
+ private UserService userService;
124
125
125
- Cucumber Spring creates an application context. This application context is
126
- shared between scenarios.
126
+ @Autowired
127
+ private TestUserInformation testUserInformation;
127
128
128
- To prevent sharing state between scenarios, beans containing glue code
129
- (i.e. step definitions, hooks, ect) are bound to the ` cucumber-glue ` scope.
129
+ @Given (" there is a user" )
130
+ public void there_is_as_user () {
131
+ User testUser = userService. createUser();
132
+ testUserInformation. setTestUser(testUser);
133
+ }
134
+ }
130
135
131
- The ` cucumber-glue ` scope starts prior to a scenario and end after a scenario.
132
- Beans in this scope are created prior to a scenario execution and disposed at
133
- the end of it.
136
+ public class PurchaseStepDefinitions {
134
137
135
- Changing a Spring bean's scope to ` SCOPE_CUCUMBER_GLUE ` will bind its lifecycle
136
- to the ` cucumber-glue ` scope.
138
+ @Autowired
139
+ private PurchaseService purchaseService;
137
140
138
- ``` java
139
- import org.springframework.stereotype.Component ;
140
- import org.springframework.context.annotation.Scope ;
141
- import static io.cucumber.spring.CucumberTestContext.SCOPE_CUCUMBER_GLUE ;
141
+ @Autowired
142
+ private TestUserInformation testUserInformation;
142
143
143
- @Component
144
- @Scope (SCOPE_CUCUMBER_GLUE )
145
- public class MyComponent {
144
+ @When (" the user makes a purchase" )
145
+ public void the_user_makes_a_purchase (){
146
+ Order order = ....
147
+ User user = testUserInformation. getTestUser();
148
+ purchaseService. purchase(user, order);
149
+ }
146
150
}
147
151
```
148
152
153
+ ### Dirtying the application context
154
+
149
155
If your tests do dirty the application context you can add ` @DirtiesContext ` to
150
- your test configuration.
156
+ your test configuration.
151
157
152
158
``` java
153
159
package com.example.app ;
@@ -156,28 +162,25 @@ import org.springframework.beans.factory.annotation.Autowired;
156
162
import org.springframework.test.annotation.DirtiesContext ;
157
163
import org.springframework.boot.test.context.SpringBootTest ;
158
164
165
+ import io.cucumber.spring.CucumberContextConfiguration ;
166
+
167
+ @CucumberContextConfiguration
159
168
@SpringBootTest (classes = TestConfig . class)
160
169
@DirtiesContext
161
- public class SomeServiceStepDefinitions {
162
-
163
- @Autowired
164
- private Belly belly; // Each scenario have a new instance of Belly
165
-
166
- [... ]
167
-
170
+ public class CucumberSpringConfiguration {
171
+
168
172
}
169
173
```
174
+ ``` java
175
+ package com.example.app ;
170
176
171
- ### XML Configuration
177
+ public class MyStepDefinitions {
172
178
173
- If you are using xml based configuration, you can to register the beans in a
174
- ` cucumber.xml ` file:
179
+ @Autowired
180
+ private MyService myService; // Each scenario have a new instance of MyService
175
181
176
- ``` xml
177
- <bean class =" com.example.app.MyService" />
178
- <bean class =" com.example.lib.SomeOtherService" />
182
+ }
179
183
```
180
184
181
- Annotate a configuration class with
182
- ` @ContextConfiguration("classpath:cucumber.xml") `
183
-
185
+ Note: Using ` @DirtiesContext ` in combination with parallel execution will lead
186
+ to undefined behaviour.
0 commit comments