Skip to content

Commit cf05cef

Browse files
committed
Setup step delcarations for cucumber-java-lambda
1 parent 549fe86 commit cf05cef

File tree

10 files changed

+983
-1
lines changed

10 files changed

+983
-1
lines changed

java-lambda/README.md

+253
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
Cucumber Java8
2+
==============
3+
4+
Provides lambda based step definitions. To use add the `cucumber-java8` dependency to your pom.xml:
5+
6+
```xml
7+
<dependencies>
8+
[...]
9+
<dependency>
10+
<groupId>io.cucumber</groupId>
11+
<artifactId>cucumber-java8</artifactId>
12+
<version>${cucumber.version}</version>
13+
<scope>test</scope>
14+
</dependency>
15+
[...]
16+
</dependencies>
17+
```
18+
19+
## Step Definitions
20+
21+
Declare a step definition calling a method in the constructor of the glue class.
22+
For localized methods import the interface from `io.cucumber.java8.<ISO2 Language Code>`
23+
24+
Data tables and Docstrings from Gherkin can be accessed by using a `DataTable`
25+
or `DocString` object as the last parameter.
26+
27+
```java
28+
package com.example.app;
29+
30+
import io.cucumber.java8.En;
31+
import io.cucumber.datatable.DataTable;
32+
import io.cucumber.docstring.DocString;
33+
34+
import static org.junit.jupiter.api.Assertions.assertEquals;
35+
36+
public class StepDefinitions implements En {
37+
38+
private RpnCalculator calc;
39+
40+
public RpnCalculatorSteps() {
41+
Given("a calculator I just turned on", () -> {
42+
calc = new RpnCalculator();
43+
});
44+
45+
When("I add {int} and {int}", (Integer arg1, Integer arg2) -> {
46+
calc.push(arg1);
47+
calc.push(arg2);
48+
calc.push("+");
49+
});
50+
51+
Then("the result is {double}", (Double expected) -> assertEquals(expected, calc.value()));
52+
53+
Given("the previous entries:", (DataTable dataTable) -> {
54+
List<Entry> entries = dataTable.asList(Entry.class);
55+
...
56+
});
57+
58+
Then("the calculation log displays:", (DocString docString) -> {
59+
...
60+
});
61+
}
62+
}
63+
```
64+
65+
## Hooks
66+
67+
Declare hooks that will be executed before/after each scenario/step by calling a
68+
method in the constructor. The method may declare an argument of type `io.cucumber.java8.Scenario`.
69+
70+
* `Before`
71+
* `After`
72+
* `BeforeStep`
73+
* `AfterStep`
74+
75+
## Transformers
76+
77+
### Parameter Type
78+
79+
Step definition parameter types can be declared by using `ParameterType`.
80+
81+
```java
82+
package com.example.app;
83+
84+
import io.cucumber.java8.En;
85+
86+
import static org.junit.jupiter.api.Assertions.assertEquals;
87+
88+
public class StepDefinitions implements En {
89+
90+
public StepDefinitions() {
91+
ParameterType("amount", "(\\d+\\.\\d+)\\s([a-zA-Z]+)", (String[] values) ->
92+
new Amount(new BigDecimal(values[0]), Currency.getInstance(values[1])));
93+
}
94+
}
95+
```
96+
97+
### Data Table Type
98+
99+
Data table types can be declared by calling `DataTableType` in the constructor.
100+
Depending on the lambda type this will be either a:
101+
* `String` -> `io.cucumber.datatable.TableCellTranformer`
102+
* `Map<String,String>` -> `io.cucumber.datatable.TableEntry`
103+
* `List<String>` -> `io.cucumber.datatable.TableRow`
104+
* `DataTable` -> `io.cucumber.datatable.TableTransformer`
105+
106+
```java
107+
package com.example.app;
108+
109+
import io.cucumber.java8.En;
110+
111+
import java.util.Map;
112+
113+
import static org.junit.jupiter.api.Assertions.assertEquals;
114+
115+
public class StepDefinitions implements En {
116+
117+
public StepDefinitions() {
118+
DataTableType((Map<String, String> row) -> new Grocery(
119+
row.get("name"),
120+
Price.fromString(row.get("price"))
121+
));
122+
}
123+
}
124+
```
125+
126+
### Default Transformers
127+
128+
Default transformers allow you to specific a transformer that will be used when
129+
there is no transform defined. This can be combined with an object mapper like
130+
Jackson to quickly transform well known string representations to Java objects.
131+
132+
* `DefaultParameterTransformer`
133+
* `DefaultDataTableEntryTransformer`
134+
* `DefaultDataTableCellTransformer`
135+
136+
For a full list of transformations that can be achieved with data table types
137+
see [cucumber/datatable](https://github.com/cucumber/cucumber/tree/master/datatable)
138+
139+
```java
140+
package com.example.app;
141+
142+
import com.fasterxml.jackson.databind.ObjectMapper;
143+
144+
import io.cucumber.java8.En;
145+
146+
public class StepDefinitions implements En {
147+
148+
public StepDefinitions() {
149+
final ObjectMapper objectMapper = new ObjectMapper();
150+
151+
DefaultParameterTransformer((fromValue, toValueType) ->
152+
objectMapper.convertValue(fromValue, objectMapper.constructType(toValueType))
153+
);
154+
}
155+
}
156+
```
157+
158+
### Empty Cells
159+
160+
Data tables in Gherkin can not represent null or the empty string unambiguously.
161+
Cucumber will interpret empty cells as `null`.
162+
163+
Empty string be represented using a replacement. For example `[empty]`.
164+
The replacement can be configured providing the `replaceWithEmptyString`
165+
argument of `DataTableType`, `DefaultDataTableCellTransformer` and
166+
`DefaultDataTableEntryTransformer`. By default no replacement is configured.
167+
168+
```gherkin
169+
Given some authors
170+
| name | first publication |
171+
| Aspiring Author | |
172+
| Ancient Author | [blank] |
173+
```
174+
175+
```java
176+
package com.example.app;
177+
178+
import io.cucumber.datatable.DataTable;
179+
180+
import io.cucumber.java8.En;
181+
182+
import java.util.List;
183+
import java.util.Map;
184+
import java.util.Optional;
185+
186+
import static org.junit.jupiter.api.Assertions.assertEquals;
187+
import static org.junit.jupiter.api.Assertions.assertNotSame;
188+
import static org.junit.jupiter.api.Assertions.assertSame;
189+
import static org.junit.jupiter.api.Assertions.assertTrue;
190+
191+
public class StepDefinitions implements En {
192+
193+
public StepDefinitions() {
194+
DataTableType("[blank]", (Map<String, String> entry) -> new Author(
195+
entry.get("name"),
196+
entry.get("first publication")
197+
));
198+
199+
Given("some authors", (DataTable authorsTable) -> {
200+
List<Author> authors = authorsTable.asList(Author.class);
201+
// authors = [Author(name="Aspiring Author", firstPublication=null), Author(name="Ancient Author", firstPublication=)]
202+
203+
});
204+
}
205+
}
206+
```
207+
208+
# Transposing Tables
209+
210+
A data table can be transposed by calling `.transpose()`. This means the keys
211+
will be in the first column rather then the first row.
212+
213+
```gherkin
214+
Given the user is
215+
| firstname | Roberto |
216+
| lastname | Lo Giacco |
217+
| nationality | Italian |
218+
```
219+
220+
And a data table type to create a User
221+
222+
```java
223+
package com.example.app;
224+
225+
import io.cucumber.datatable.DataTable;
226+
227+
import io.cucumber.java8.En;
228+
229+
import java.util.List;
230+
import java.util.Map;
231+
import java.util.Optional;
232+
233+
import static org.junit.jupiter.api.Assertions.assertEquals;
234+
import static org.junit.jupiter.api.Assertions.assertNotSame;
235+
import static org.junit.jupiter.api.Assertions.assertSame;
236+
import static org.junit.jupiter.api.Assertions.assertTrue;
237+
238+
public class StepDefinitions implements En {
239+
240+
public StepDefinitions() {
241+
DataTableType((Map<String, String> entry) -> new User(
242+
entry.get("firstname"),
243+
entry.get("lastname")
244+
entry.get("nationality")
245+
));
246+
247+
Given("the user is", (DataTable authorsTable) -> {
248+
User user = authorsTable.transpose().asList(User.class);
249+
// user = User(firstname="Roberto", lastname="Lo Giacco", nationality="Italian")
250+
});
251+
}
252+
}
253+
```

java-lambda/pom.xml

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
2+
<modelVersion>4.0.0</modelVersion>
3+
4+
<parent>
5+
<groupId>io.cucumber</groupId>
6+
<artifactId>cucumber-jvm</artifactId>
7+
<version>7.3.3-SNAPSHOT</version>
8+
</parent>
9+
10+
<artifactId>cucumber-java-lambda</artifactId>
11+
<packaging>jar</packaging>
12+
<name>Cucumber-JVM: Java Lambda</name>
13+
14+
<properties>
15+
<project.Automatic-Module-Name>io.cucumber.lambda</project.Automatic-Module-Name>
16+
<apiguardian-api.version>1.1.2</apiguardian-api.version>
17+
<junit-jupiter.version>5.8.2</junit-jupiter.version>
18+
<typetools.version>0.6.3</typetools.version>
19+
<assertj.version>3.22.0</assertj.version>
20+
</properties>
21+
22+
<dependencyManagement>
23+
<dependencies>
24+
<dependency>
25+
<groupId>io.cucumber</groupId>
26+
<artifactId>cucumber-bom</artifactId>
27+
<version>${project.version}</version>
28+
<type>pom</type>
29+
<scope>import</scope>
30+
</dependency>
31+
<dependency>
32+
<groupId>org.junit</groupId>
33+
<artifactId>junit-bom</artifactId>
34+
<version>${junit-jupiter.version}</version>
35+
<type>pom</type>
36+
<scope>import</scope>
37+
</dependency>
38+
</dependencies>
39+
</dependencyManagement>
40+
41+
<dependencies>
42+
<dependency>
43+
<groupId>io.cucumber</groupId>
44+
<artifactId>cucumber-core</artifactId>
45+
</dependency>
46+
<dependency>
47+
<groupId>org.apiguardian</groupId>
48+
<artifactId>apiguardian-api</artifactId>
49+
<version>${apiguardian-api.version}</version>
50+
</dependency>
51+
<dependency>
52+
<groupId>net.jodah</groupId>
53+
<artifactId>typetools</artifactId>
54+
<version>${typetools.version}</version>
55+
</dependency>
56+
57+
<dependency>
58+
<groupId>io.cucumber</groupId>
59+
<artifactId>cucumber-junit-platform-engine</artifactId>
60+
<scope>test</scope>
61+
</dependency>
62+
<dependency>
63+
<groupId>org.junit.platform</groupId>
64+
<artifactId>junit-platform-suite</artifactId>
65+
<scope>test</scope>
66+
</dependency>
67+
<dependency>
68+
<groupId>org.junit.jupiter</groupId>
69+
<artifactId>junit-jupiter</artifactId>
70+
<scope>test</scope>
71+
</dependency>
72+
73+
<dependency>
74+
<groupId>org.assertj</groupId>
75+
<artifactId>assertj-core</artifactId>
76+
<version>${assertj.version}</version>
77+
</dependency>
78+
</dependencies>
79+
80+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package io.cucumber.lambda;
2+
3+
import io.cucumber.core.backend.CucumberBackendException;
4+
import io.cucumber.core.backend.CucumberInvocationTargetException;
5+
import io.cucumber.core.backend.Located;
6+
7+
import java.lang.reflect.InvocationTargetException;
8+
import java.lang.reflect.Method;
9+
10+
final class Invoker {
11+
12+
private Invoker() {
13+
14+
}
15+
16+
static Object invoke(Located located, Object target, Method method, Object... args) {
17+
boolean accessible = method.canAccess(target);
18+
try {
19+
method.setAccessible(true);
20+
return method.invoke(target, args);
21+
} catch (IllegalArgumentException | IllegalAccessException e) {
22+
throw new CucumberBackendException("Failed to invoke " + located.getLocation(), e);
23+
} catch (InvocationTargetException e) {
24+
throw new CucumberInvocationTargetException(located, e);
25+
} finally {
26+
method.setAccessible(accessible);
27+
}
28+
}
29+
30+
}

0 commit comments

Comments
 (0)