Skip to content

Commit 68f4cf8

Browse files
committed
Simple Cucumber-based BDD tests for the web services
1 parent b9bd9e8 commit 68f4cf8

File tree

19 files changed

+475
-55
lines changed

19 files changed

+475
-55
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
# BDD in Action Sample code - chapter 9
22

3+
This chapter focuses on back-end and headless testing.

flight-status-service/README.md

+9-1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,12 @@ It runs using an embedded Tomcat server.
88

99
-----
1010
mvn clean package tomcat:run
11-
-----
11+
-----
12+
13+
You can check that the web services are available by using the following URL:
14+
15+
-----
16+
http://localhost:8080/rest/flights/status
17+
-----
18+
19+

flight-status-service/pom.xml

+100-22
Original file line numberDiff line numberDiff line change
@@ -12,38 +12,39 @@
1212

1313
<!-- Jersey -->
1414
<dependency>
15-
<groupId>com.sun.jersey</groupId>
16-
<artifactId>jersey-server</artifactId>
17-
<version>1.17</version>
15+
<groupId>org.glassfish.jersey</groupId>
16+
<artifactId>jersey-bom</artifactId>
17+
<version>2.4.1</version>
18+
<type>pom</type>
1819
</dependency>
1920
<dependency>
20-
<groupId>com.sun.jersey</groupId>
21-
<artifactId>jersey-servlet</artifactId>
22-
<version>1.17</version>
21+
<groupId>org.glassfish.jersey.core</groupId>
22+
<artifactId>jersey-server</artifactId>
23+
<version>2.4.1</version>
2324
</dependency>
2425
<dependency>
25-
<groupId>com.sun.jersey</groupId>
26-
<artifactId>jersey-client</artifactId>
27-
<version>1.17</version>
26+
<groupId>org.glassfish.jersey.containers</groupId>
27+
<artifactId>jersey-container-servlet</artifactId>
28+
<version>2.4.1</version>
2829
</dependency>
2930
<dependency>
30-
<groupId>com.sun.jersey</groupId>
31-
<artifactId>jersey-json</artifactId>
32-
<version>1.17</version>
31+
<groupId>com.fasterxml.jackson.jaxrs</groupId>
32+
<artifactId>jackson-jaxrs-json-provider</artifactId>
33+
<version>2.3.0</version>
3334
</dependency>
3435

35-
<!-- Using HK2 for dependency injection -->
36+
<!-- Jersey client -->
3637
<dependency>
37-
<groupId>org.glassfish.hk2</groupId>
38-
<artifactId>hk2</artifactId>
39-
<version>2.2.0-b25</version>
38+
<groupId>org.glassfish.jersey.core</groupId>
39+
<artifactId>jersey-client</artifactId>
40+
<version>2.4.1</version>
4041
</dependency>
4142

4243
<!-- JAX-RS -->
4344
<dependency>
4445
<groupId>javax.ws.rs</groupId>
45-
<artifactId>jsr311-api</artifactId>
46-
<version>1.1.1</version>
46+
<artifactId>javax.ws.rs-api</artifactId>
47+
<version>2.0</version>
4748
</dependency>
4849

4950
<!-- Various -->
@@ -57,6 +58,12 @@
5758
<artifactId>guava</artifactId>
5859
<version>15.0</version>
5960
</dependency>
61+
<dependency>
62+
<groupId>com.googlecode.lambdaj</groupId>
63+
<artifactId>lambdaj</artifactId>
64+
<version>2.3.3</version>
65+
</dependency>
66+
6067
<dependency>
6168
<groupId>commons-logging</groupId>
6269
<artifactId>commons-logging</artifactId>
@@ -69,6 +76,13 @@
6976
</exclusions>
7077
</dependency>
7178

79+
<!-- Cucumber -->
80+
<dependency>
81+
<groupId>info.cukes</groupId>
82+
<artifactId>cucumber-picocontainer</artifactId>
83+
<version>1.1.5</version>
84+
<scope>test</scope>
85+
</dependency>
7286
<dependency>
7387
<groupId>info.cukes</groupId>
7488
<artifactId>cucumber-junit</artifactId>
@@ -81,6 +95,12 @@
8195
<version>4.11</version>
8296
<scope>test</scope>
8397
</dependency>
98+
<dependency>
99+
<groupId>org.skyscreamer</groupId>
100+
<artifactId>jsonassert</artifactId>
101+
<version>1.2.1</version>
102+
<scope>test</scope>
103+
</dependency>
84104

85105
<!-- Unit testing in Spock -->
86106
<dependency>
@@ -143,25 +163,83 @@
143163
</configuration>
144164
</plugin>
145165

166+
<!-- Use the Failsafe plugin to distinguish unit from integration/acceptance tests -->
167+
<plugin>
168+
<artifactId>maven-failsafe-plugin</artifactId>
169+
<version>2.16</version>
170+
<executions>
171+
<execution>
172+
<goals>
173+
<goal>integration-test</goal>
174+
<goal>verify</goal>
175+
</goals>
176+
</execution>
177+
</executions>
178+
</plugin>
179+
180+
<!-- Place the integration and acceptance tests in the it directory -->
181+
<plugin>
182+
<groupId>org.codehaus.mojo</groupId>
183+
<artifactId>build-helper-maven-plugin</artifactId>
184+
<version>1.8</version>
185+
<executions>
186+
<execution>
187+
<id>add-source</id>
188+
<phase>generate-sources</phase>
189+
<goals>
190+
<goal>add-test-source</goal>
191+
</goals>
192+
<configuration>
193+
<sources>
194+
<source>src/it/java</source>
195+
</sources>
196+
</configuration>
197+
</execution>
198+
<execution>
199+
<id>add-resource</id>
200+
<phase>generate-sources</phase>
201+
<goals>
202+
<goal>add-test-resource</goal>
203+
</goals>
204+
<configuration>
205+
<resources>
206+
<resource>
207+
<directory>src/it/resources</directory>
208+
</resource>
209+
</resources>
210+
</configuration>
211+
</execution>
212+
</executions>
213+
</plugin>
214+
146215
<!-- Embedded Jetty instance -->
147216
<plugin>
148217
<groupId>org.eclipse.jetty</groupId>
149218
<artifactId>jetty-maven-plugin</artifactId>
150219
<version>9.1.0.v20131115</version>
220+
<configuration>
221+
<scanIntervalSeconds>10</scanIntervalSeconds>
222+
<stopKey>foo</stopKey>
223+
<stopPort>9999</stopPort>
224+
</configuration>
151225
<executions>
152226
<execution>
153-
<id>jetty-run</id>
227+
<id>start-jetty</id>
228+
<phase>pre-integration-test</phase>
154229
<goals>
155230
<goal>start</goal>
156231
</goals>
157-
<phase>pre-integration-test</phase>
232+
<configuration>
233+
<scanIntervalSeconds>0</scanIntervalSeconds>
234+
<daemon>true</daemon>
235+
</configuration>
158236
</execution>
159237
<execution>
160-
<id>jetty-shutdown</id>
238+
<id>stop-jetty</id>
239+
<phase>post-integration-test</phase>
161240
<goals>
162241
<goal>stop</goal>
163242
</goals>
164-
<phase>post-integration-test</phase>
165243
</execution>
166244
</executions>
167245
</plugin>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.wakaleo.bddinaction.chapter9.flightstatus;
2+
3+
import cucumber.api.junit.Cucumber;
4+
import org.junit.runner.RunWith;
5+
6+
@RunWith(Cucumber.class)
7+
@Cucumber.Options(format = { "pretty", "html:target/cucumber-html-report", "json:target/report.json" })
8+
public class FlightStatusFeaturesIT {
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.wakaleo.bddinaction.chapter9.flightstatus.client;
2+
3+
4+
import com.wakaleo.bddinaction.chapter9.flightstatus.model.Flight;
5+
import com.wakaleo.bddinaction.chapter9.flightstatus.model.FlightType;
6+
7+
import javax.ws.rs.client.Client;
8+
import javax.ws.rs.client.ClientBuilder;
9+
import javax.ws.rs.client.WebTarget;
10+
import javax.ws.rs.core.GenericType;
11+
import java.lang.reflect.GenericArrayType;
12+
import java.lang.reflect.Type;
13+
import java.util.ArrayList;
14+
import java.util.Arrays;
15+
import java.util.List;
16+
17+
public class FlightStatusClient {
18+
19+
private final String BASE_URL = "http://localhost:8080/rest/flights";
20+
21+
public Flight findByFlightNumber(String flightNumber) {
22+
Client client = ClientBuilder.newClient();
23+
WebTarget webTarget = client.target(BASE_URL).path(flightNumber);
24+
return webTarget.request().buildGet().invoke(Flight.class);
25+
}
26+
27+
public String findByFlightNumberInJsonFormat(String flightNumber) {
28+
Client client = ClientBuilder.newClient();
29+
WebTarget webTarget = client.target(BASE_URL).path(flightNumber);
30+
return webTarget.request().buildGet().invoke(String.class);
31+
}
32+
33+
private GenericArrayType listOfFlights() {
34+
return new GenericArrayType() {
35+
@Override
36+
public Type getGenericComponentType() {
37+
return Flight.class;
38+
}
39+
};
40+
}
41+
public List<Flight> findByDepartureCityAndType(String departure, FlightType type) {
42+
Client client = ClientBuilder.newClient();
43+
WebTarget webTarget = client.target(BASE_URL)
44+
.path("from/" + departure)
45+
.queryParam("flightType", type);
46+
Flight[] flights = ((Flight[]) webTarget.request().buildGet().invoke(new GenericType(listOfFlights())));
47+
48+
return new ArrayList(Arrays.asList(flights));
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.wakaleo.bddinaction.chapter9.flightstatus.steps;
2+
3+
import com.wakaleo.bddinaction.chapter9.flightstatus.client.FlightStatusClient;
4+
import com.wakaleo.bddinaction.chapter9.flightstatus.model.Flight;
5+
import cucumber.api.DataTable;
6+
import cucumber.api.java.en.Given;
7+
import cucumber.api.java.en.Then;
8+
import cucumber.api.java.en.When;
9+
import org.json.JSONException;
10+
import org.skyscreamer.jsonassert.JSONAssert;
11+
import org.skyscreamer.jsonassert.JSONCompareMode;
12+
13+
import static com.google.common.collect.Lists.newArrayList;
14+
15+
public class FlightDetailsSteps {
16+
17+
String flightNumber;
18+
Flight matchingFlight;
19+
FlightStatusClient client = new FlightStatusClient();
20+
21+
@Given("^I need to know the details of flight number (.*)$")
22+
public void the_flight_number(String flightNumber) throws Throwable {
23+
this.flightNumber = flightNumber;
24+
}
25+
26+
@When("^I request the details about this flight$")
27+
public void I_request_the_details_about_this_flight() throws Throwable {
28+
matchingFlight = client.findByFlightNumber(flightNumber);
29+
}
30+
31+
@Then("^I should receive the following:$")
32+
public void I_should_receive_the_following_details(DataTable flightDetails) throws Throwable {
33+
flightDetails.diff(newArrayList(matchingFlight));
34+
}
35+
36+
String matchingFlightInJson;
37+
@When("^I request the details about this flight in JSON format$")
38+
public void request_details_in_json_format() {
39+
matchingFlightInJson = client.findByFlightNumberInJsonFormat(flightNumber);
40+
}
41+
42+
@Then("^I should receive:$")
43+
public void should_receive_json_data(String expectedJsonData) throws JSONException {
44+
JSONAssert.assertEquals(expectedJsonData, matchingFlightInJson, JSONCompareMode.LENIENT);
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.wakaleo.bddinaction.chapter9.flightstatus.steps;
2+
3+
import com.wakaleo.bddinaction.chapter9.flightstatus.client.FlightStatusClient;
4+
import com.wakaleo.bddinaction.chapter9.flightstatus.model.FlightType;
5+
import cucumber.api.DataTable;
6+
import cucumber.api.java.en.Given;
7+
import cucumber.api.java.en.Then;
8+
import cucumber.api.java.en.When;
9+
10+
import java.util.List;
11+
12+
public class ScheduledFlightsSteps {
13+
14+
String departure;
15+
List retrievedFlights;
16+
17+
@Given("^I want to know the flights out of (.*)$")
18+
public void I_want_to_check_the_flights_out_o_(String departure) throws Throwable {
19+
this.departure = departure;
20+
}
21+
22+
@When("^I request the (.*) flights$")
23+
public void I_request_flights_of_type(FlightType flightType) throws Throwable {
24+
FlightStatusClient client = new FlightStatusClient();
25+
retrievedFlights = client.findByDepartureCityAndType(departure, flightType);
26+
}
27+
28+
@Then("^I should see the following flights:$")
29+
public void I_should_see_the_following_flights(DataTable expectedFlights) throws Throwable {
30+
expectedFlights.diff(retrievedFlights);
31+
}
32+
33+
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.wakaleo.bddinaction.chapter9.flightstatus.transformers;
2+
3+
import cucumber.api.Transformer;
4+
import cucumber.runtime.ParameterInfo;
5+
import org.joda.time.LocalDate;
6+
import org.joda.time.format.DateTimeFormat;
7+
import org.joda.time.format.DateTimeFormatter;
8+
9+
import java.util.Locale;
10+
11+
public class JodaTransformer extends Transformer<LocalDate> {
12+
13+
private String format;
14+
15+
@Override
16+
public void setParameterInfoAndLocale(ParameterInfo parameterInfo, Locale locale) {
17+
super.setParameterInfoAndLocale(parameterInfo, locale);
18+
this.format = parameterInfo.getFormat();
19+
}
20+
21+
public LocalDate transform(String value) {
22+
return DateTimeFormat.forPattern(format).parseLocalDate(value);
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Feature: Retrieve information about a given flight
2+
3+
Scenario: Find flight details by flight number
4+
Given I need to know the details of flight number FH-101
5+
When I request the details about this flight
6+
Then I should receive the following:
7+
| flightNumber | departure | destination | time |
8+
| FH-101 | MEL | SYD | 06:00 |
9+
10+
11+
Scenario: Should return flight details in JSON form
12+
Given I need to know the details of flight number FH-102
13+
When I request the details about this flight in JSON format
14+
Then I should receive:
15+
"""
16+
{
17+
"flightNumber":"FH-102",
18+
"departure":"SYD",
19+
"destination":"MEL",
20+
"time":"06:15"
21+
}
22+
"""

0 commit comments

Comments
 (0)