Skip to content

Commit 928bd24

Browse files
Test/cucumber get post (#42)
* feat(config_cucumber): add cucumber dependencies * test(cucumber_get_post): add one integration test for post * test(cucumber_get_post): exclude springintegration test file for sonar * test(cucumber_get_post): add documentation to launch cucumber test * test(cucumber_get_post): ajout des tests sur le get * test(cucumber_get_post): use datatable instead of chaining And * test(cucumber_get_post): suppression des librairies non utilisées * test(cucumber_get_post): change schema.sql to use users instead of USERS * test(cucumber_get_post): remove cucumber tests form sonar build --------- Co-authored-by: Romain Vanhee <romain.vanhee@yrycom.com>
1 parent a6d7640 commit 928bd24

10 files changed

Lines changed: 224 additions & 8 deletions

File tree

.github/workflows/sonar.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,4 @@ jobs:
4040
mkdir -p src/test/resources
4141
echo ${{ secrets.APPLICATION_TEST_PROPERTIES }} | base64 -d > src/test/resources/application-test.properties
4242
echo "spring.sql.init.mode=never" >> src/test/resources/application-test.properties
43-
mvn clean verify sonar:sonar -DskipDocker=true -Dsonar.qualitygate.wait=true
43+
mvn clean verify sonar:sonar -DskipDocker=true -Dsonar.qualitygate.wait=true -Dit.test="!CucumberIntegrationTest"

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,13 +132,16 @@ curl -X PUT "http://localhost:8080/random-users/1" \
132132

133133
# With coverage report (target/site/jacoco/index.html)
134134
./mvnw clean verify
135+
136+
#cucumber tests
137+
./mvnw verify -DskipDocker=true -Dit.test=CucumberIntegrationTest
135138
```
136139

137140
**Test Stack:**
138141
- JUnit 5 - Testing framework
139142
- Mockito - Mocking
140143
- JaCoCo - Code coverage
141-
- Cucumber - BDD (planned)
144+
- Cucumber - BDD
142145

143146
---
144147

pom.xml

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
<java.version>25</java.version>
3131
<skipDocker>false</skipDocker>
3232
<sonar.coverage.exclusions>**/*Application.java</sonar.coverage.exclusions>
33+
<sonar.test.exclusions>**/feature/SpringIntegrationTest.java</sonar.test.exclusions>
3334
<sonar.coverage.jacoco.xmlReportPaths>${project.build.directory}/site/jacoco/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>
3435
</properties>
3536
<dependencies>
@@ -83,9 +84,15 @@
8384
<artifactId>spring-boot-starter-data-jdbc-test</artifactId>
8485
<scope>test</scope>
8586
</dependency>
87+
<dependency>
88+
<groupId>org.springframework.boot</groupId>
89+
<artifactId>spring-boot-starter-webmvc-test</artifactId>
90+
<scope>test</scope>
91+
</dependency>
8692
<dependency>
87-
<groupId>org.springframework.boot</groupId>
88-
<artifactId>spring-boot-starter-webmvc-test</artifactId>
93+
<groupId>io.cucumber</groupId>
94+
<artifactId>cucumber-spring</artifactId>
95+
<version>7.14.0</version>
8996
<scope>test</scope>
9097
</dependency>
9198
<dependency>
@@ -96,16 +103,26 @@
96103
</dependency>
97104
<dependency>
98105
<groupId>io.cucumber</groupId>
99-
<artifactId>cucumber-junit</artifactId>
106+
<artifactId>cucumber-junit-platform-engine</artifactId>
100107
<version>7.14.0</version>
101108
<scope>test</scope>
102109
</dependency>
103110
<dependency>
104-
<groupId>io.cucumber</groupId>
105-
<artifactId>cucumber-spring</artifactId>
106-
<version>7.14.0</version>
111+
<groupId>org.junit.platform</groupId>
112+
<artifactId>junit-platform-suite</artifactId>
107113
<scope>test</scope>
108114
</dependency>
115+
<dependency>
116+
<groupId>org.springframework.boot</groupId>
117+
<artifactId>spring-boot-restclient</artifactId>
118+
<scope>test</scope>
119+
</dependency>
120+
<dependency>
121+
<groupId>org.springframework.boot</groupId>
122+
<artifactId>spring-boot-starter-test</artifactId>
123+
<scope>test</scope>
124+
</dependency>
125+
109126
</dependencies>
110127

111128
<build>

src/main/java/com/xpeho/spring_boot_java_random_user/data/models/db/User.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,23 @@
77
@Table("users")
88
public class User {
99
@Id
10+
@Column("id")
1011
private Long id;
12+
@Column("gender")
1113
private String gender;
1214
@Column("firstname")
1315
private String firstname;
1416
@Column("lastname")
1517
private String lastname;
18+
@Column("civility")
1619
private String civility;
20+
@Column("email")
1721
private String email;
22+
@Column("phone")
1823
private String phone;
24+
@Column("picture")
1925
private String picture;
26+
@Column("nationality")
2027
private String nationality;
2128

2229
// Required by Spring Data JDBC to instantiate the entity via reflection
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package feature;
2+
3+
import org.junit.platform.suite.api.ConfigurationParameter;
4+
import org.junit.platform.suite.api.IncludeEngines;
5+
import org.junit.platform.suite.api.SelectClasspathResource;
6+
import org.junit.platform.suite.api.Suite;
7+
8+
import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME;
9+
import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME;
10+
11+
@Suite
12+
@IncludeEngines("cucumber")
13+
@SelectClasspathResource("features")
14+
@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "feature")
15+
@ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty")
16+
public class CucumberIntegrationTest {
17+
18+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package feature;
2+
3+
import com.xpeho.spring_boot_java_random_user.SpringBootJavaRandomUserApplication;
4+
import io.cucumber.spring.CucumberContextConfiguration;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.boot.resttestclient.TestRestTemplate;
7+
import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureTestRestTemplate;
8+
import org.springframework.boot.test.context.SpringBootTest;
9+
import org.springframework.boot.test.web.server.LocalServerPort;
10+
import org.springframework.http.HttpEntity;
11+
import org.springframework.http.HttpHeaders;
12+
import org.springframework.http.MediaType;
13+
import org.springframework.http.ResponseEntity;
14+
import org.springframework.test.context.ActiveProfiles;
15+
16+
@CucumberContextConfiguration
17+
@ActiveProfiles("test")
18+
@AutoConfigureTestRestTemplate
19+
@SpringBootTest(
20+
classes = SpringBootJavaRandomUserApplication.class,
21+
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
22+
properties = {
23+
"spring.sql.init.mode=always",
24+
"spring.sql.init.schema-locations=classpath:schema-h2.sql"
25+
}
26+
)
27+
public class SpringIntegrationTest {
28+
29+
@Autowired
30+
protected TestRestTemplate restTemplate;
31+
32+
@LocalServerPort
33+
protected int port;
34+
35+
protected ResponseEntity<String> latestResponse;
36+
37+
protected void executeGet(String path) {
38+
String url = "http://localhost:" + port + path;
39+
latestResponse = restTemplate.getForEntity(url, String.class);
40+
}
41+
42+
protected void executePost(String path, Object payload) {
43+
String url = "http://localhost:" + port + path;
44+
HttpHeaders headers = new HttpHeaders();
45+
headers.setContentType(MediaType.APPLICATION_JSON);
46+
HttpEntity<Object> request = new HttpEntity<>(payload, headers);
47+
latestResponse = restTemplate.postForEntity(url, request, String.class);
48+
}
49+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package feature;
2+
3+
import com.fasterxml.jackson.databind.JsonNode;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import com.xpeho.spring_boot_java_random_user.domain.entities.UserRequest;
6+
import io.cucumber.datatable.DataTable;
7+
import io.cucumber.java.en.And;
8+
import io.cucumber.java.en.Given;
9+
import io.cucumber.java.en.Then;
10+
import io.cucumber.java.en.When;
11+
12+
import java.util.List;
13+
14+
import static org.junit.jupiter.api.Assertions.assertEquals;
15+
import static org.junit.jupiter.api.Assertions.assertNotNull;
16+
import static org.junit.jupiter.api.Assertions.assertTrue;
17+
18+
public class StepDefinition extends SpringIntegrationTest {
19+
20+
private final ObjectMapper objectMapper = new ObjectMapper();
21+
private UserRequest payload;
22+
private Long createdUserId;
23+
24+
@Given("a valid user payload for creation")
25+
public void aValidUserPayloadForCreation() {
26+
payload = new UserRequest(
27+
"female",
28+
"Emma",
29+
"Stone",
30+
"Ms",
31+
"emma@example.com",
32+
"0644444444",
33+
"emma.jpg",
34+
"FR"
35+
);
36+
}
37+
38+
@When("the client call to POST \\/random-users")
39+
public void theClientCallToPostRandomUser() {
40+
executePost("/random-users", payload);
41+
}
42+
43+
@Then("the response status should be {int}")
44+
public void theResponseStatusShouldBe(int expectedStatus) {
45+
assertEquals(expectedStatus, latestResponse.getStatusCode().value());
46+
}
47+
48+
@And("the user profile")
49+
public void theUserProfile(DataTable table) throws Exception{
50+
JsonNode body = objectMapper.readTree(latestResponse.getBody());
51+
List<List<String>> rows = table.asLists(String.class);
52+
for (List<String> row : rows) {
53+
String field = row.get(0).trim();
54+
String expected = row.get(1).trim();
55+
JsonNode valueNode = body.get(field);
56+
assertNotNull(valueNode);
57+
58+
if ("<generated_id>".equals(expected)) {
59+
createdUserId = valueNode.asLong();
60+
assertTrue(createdUserId > 0);
61+
continue;
62+
}
63+
if ("<created_id>".equals(expected)) {
64+
assertNotNull(createdUserId);
65+
assertEquals(createdUserId.toString(), valueNode.asText());
66+
continue;
67+
}
68+
assertEquals(expected, valueNode.asText());
69+
}
70+
}
71+
72+
@When("the client call to GET \\/random-users\\/{int}")
73+
public void theClientCallToGetRandomUser(int id) {
74+
executeGet("/random-users/" + id);
75+
}
76+
77+
@When("the client call to GET the created user")
78+
public void theClientCallToGetTheCreatedUser() {
79+
assertNotNull(createdUserId, "No user was created before this step");
80+
executeGet("/random-users/" + createdUserId);
81+
}
82+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Feature: Create user endpoint
2+
3+
Scenario: Create a user successfully
4+
Given a valid user payload for creation
5+
When the client call to POST /random-users
6+
Then the response status should be 201
7+
And the user profile
8+
| id | <generated_id> |
9+
| firstname | Emma |
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Feature: Get user endpoint
2+
3+
Scenario: Get a user by ID after creation
4+
Given a valid user payload for creation
5+
When the client call to POST /random-users
6+
Then the response status should be 201
7+
And the user profile
8+
| id | <generated_id> |
9+
When the client call to GET the created user
10+
Then the response status should be 200
11+
And the user profile
12+
| id | <created_id> |
13+
| firstname | Emma |
14+
15+
Scenario: Get a user that does not exist
16+
When the client call to GET /random-users/999
17+
Then the response status should be 404

src/test/resources/schema-h2.sql

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
DROP TABLE IF EXISTS "users";
2+
3+
CREATE TABLE IF NOT EXISTS "users"
4+
(
5+
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
6+
gender VARCHAR(20),
7+
firstname VARCHAR(100),
8+
lastname VARCHAR(100),
9+
civility VARCHAR(20),
10+
email VARCHAR(255),
11+
phone VARCHAR(50),
12+
picture VARCHAR(500),
13+
nationality VARCHAR(10)
14+
);

0 commit comments

Comments
 (0)