Skip to content

Commit 5d8cc8b

Browse files
authored
Merge pull request #12 from ifeelgood/main
Regression testsuit with Cucumber
2 parents d461b94 + 7922137 commit 5d8cc8b

24 files changed

Lines changed: 1233 additions & 82 deletions

.github/workflows/maven-verify.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
2+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven
3+
4+
# This workflow uses actions that are not certified by GitHub.
5+
# They are provided by a third-party and are governed by
6+
# separate terms of service, privacy policy, and support
7+
# documentation.
8+
9+
name: Java CI with Maven
10+
11+
on:
12+
push:
13+
branches: [ "main" ]
14+
pull_request:
15+
branches: [ "main" ]
16+
workflow_dispatch:
17+
18+
jobs:
19+
build:
20+
21+
runs-on: ubuntu-latest
22+
23+
steps:
24+
- uses: actions/checkout@v4
25+
- name: Set up JDK 17
26+
uses: actions/setup-java@v4
27+
with:
28+
java-version: '17'
29+
distribution: 'temurin'
30+
cache: maven
31+
- name: Build with Maven
32+
run: mvn -B verify --file memex/pom.xml
33+
34+
- name: Archive Jacoco reports
35+
uses: actions/upload-artifact@v4
36+
with:
37+
name: jacoco-reports
38+
path: ${{ github.workspace }}/memex/target/site/jacoco
39+
40+
- name: JaCoCo Report
41+
uses: Madrapps/jacoco-report@v1.7.2
42+
with:
43+
# Comma separated paths of the generated jacoco xml files (supports wildcard glob pattern)
44+
paths: |
45+
${{ github.workspace }}/memex/target/site/jacoco/jacoco.xml
46+
# Github personal token to add comments to Pull Request
47+
token: ${{ secrets.GITHUB_TOKEN }}
48+
title: '# Java Code Coverage Report'
49+
50+

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
hs_err_pid*
3434
replay_pid*
3535
/DataGen/.idea/
36-
/Mews/.idea/
36+
/memex/.idea/
3737
/SAMPLE_DATA/VOSA/
38-
/Mews/target/
38+
/memex/target/
3939
/DataGen/target/

memex/CONTRIBUTING.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Contributing to Memex
2+
3+
First off, thank you for considering contributing to Memex! Your help is appreciated.
4+
5+
## Guiding Principles
6+
7+
* **All contributions must be covered by tests.** This ensures the stability and reliability of the application.
8+
* Aim for high code coverage for any new or modified code.
9+
* Follow the existing code style and conventions.
10+
11+
## Testing Approach
12+
13+
This project uses a Behavior-Driven Development (BDD) approach for testing, primarily focusing on API-level integration tests. The technology stack for testing includes:
14+
15+
* **[Cucumber](https://cucumber.io/)**: Used for writing high-level test scenarios in a human-readable format called Gherkin (files with `.feature` extension). These scenarios describe the behavior of the application from a user's perspective.
16+
* Feature files are located in `src/test/resources/features/`.
17+
* Step definitions, which implement the Gherkin steps, are Java methods located in classes within `src/test/java/com/johnlpage/memex/cucumber/steps/`.
18+
19+
* **[JUnit 5](https://junit.org/junit5/)**: The foundational testing framework used to run the Cucumber tests and provide assertions. It's the default low-level testing framework for modern Java applications, including those built with Spring Boot 3.
20+
21+
* **[Rest Assured](https://rest-assured.io/)**: A Java library used within the Cucumber step definitions to test RESTful APIs. It provides a fluent and intuitive DSL for making HTTP requests and validating responses, leading to readable and concise test code.
22+
23+
* **[Testcontainers](https://www.testcontainers.org/)**: Used to manage external dependencies for tests, specifically a MongoDB instance. This ensures tests run in a consistent and isolated environment.
24+
25+
### Test Structure and Execution Flow
26+
27+
1. **Feature Files (`.feature`)**: Define test scenarios using Gherkin syntax (Given-When-Then). These describe the preconditions, actions, and expected outcomes.
28+
2. **Step Definitions (`.java`)**: Java methods annotated with Cucumber annotations (`@Given`, `@When`, `@Then`) that map Gherkin steps to executable code.
29+
* `@Given` steps often involve setting up preconditions, such as populating the MongoDB database with specific test data using `MongoTemplate`. This database is typically managed by Testcontainers.
30+
* `@When` steps typically use Rest Assured to send HTTP requests to the application's API endpoints.
31+
* `@Then` steps use Rest Assured's validation capabilities, along with JUnit 5 assertions and Hamcrest matchers, to verify the HTTP response (status code, headers, body content).
32+
3. **Test Runner**: JUnit 5 is used to discover and execute the Cucumber tests.
33+
34+
### MongoDB for Tests (Testcontainers)
35+
36+
For integration tests requiring a database, we use Testcontainers to spin up a MongoDB instance.
37+
38+
* **Default Behavior**: If the `spring.data.mongodb.uri` property in `src/test/resources/application-test.properties` is empty (which is the default), the tests will automatically attempt to start a MongoDB container using Docker.
39+
* **Requirement**: A Docker daemon must be running locally for this to work (e.g., by starting Docker Desktop).
40+
* **Using an External MongoDB**: If you do not have Docker available, or wish to use a specific external MongoDB instance for testing, you can specify its connection string by setting the `spring.data.mongodb.uri` property in `src/test/resources/application-test.properties`. In this case, Testcontainers will not attempt to start a local MongoDB container.
41+
42+
### Test Data `testid` Range
43+
44+
To ensure test data isolation and prevent unintended side effects, vehicle inspection `testid` values used during test setup and execution **must** fall within a dedicated range. This range is defined by the following properties in `src/test/resources/application-test.properties`:
45+
* `memex.test.data.vehicleinspection-testid-range.start`
46+
* `memex.test.data.vehicleinspection-testid-range.end`
47+
48+
**Important:**
49+
* When writing or modifying tests, ensure all `testid` values used for creating, querying, or deleting test data are within this configured range.
50+
* Tests **must not** attempt to read, modify, or delete data outside of this dedicated test ID range. This is crucial for preventing tests from interfering with each other or with any non-test data.
51+
* Using a `testid` outside of this range during test data manipulation will lead to test failures.
52+
53+
**Caution**: If you configure the tests to run against a real (non-Testcontainer) MongoDB instance, be aware that the tests **will modify data within this `testid` range on that MongoDB instance.** Ensure this range does not overlap with critical data if using a shared or persistent MongoDB server for testing. It is generally recommended to use Testcontainers or a dedicated, ephemeral test database to avoid accidental data modification.
54+
55+
These properties can be adjusted in `src/test/resources/application-test.properties` if necessary for specific testing needs, but ensure they define a sensible range and that all tests strictly adhere to operating only within the specified `testid` boundaries.
56+
57+
## Adding a New Test
58+
59+
To add a new Cucumber test, follow these general steps:
60+
61+
1. **Define Scenarios in a `.feature` file**:
62+
* Locate an existing relevant `.feature` file in `src/test/resources/features/` (e.g., `inspections.rest.crud.feature`, `inspections.kafka.feature`) or create a new one if testing a new feature area or interaction type.
63+
* Write your test scenarios using Gherkin syntax (Given/When/Then). Clearly describe the preconditions, actions to be performed, and expected outcomes.
64+
* Use tags (`@tagname`) to categorize your scenarios or features (e.g., `@restapi`, `@kafka`, `@sunny_day`).
65+
66+
2. **Implement Step Definitions**:
67+
* For each Gherkin step in your scenario, you'll need a corresponding Java method in a step definition class. These classes are organized by concern within `src/test/java/com/johnlpage/memex/cucumber/steps/`:
68+
* `MongoPreConditionSteps.java`: For steps setting up or verifying database state (`@Given`).
69+
* `RestApiSteps.java`: For steps interacting with the REST API (`@When`, `@Then`).
70+
* `TimeManagementSteps.java`: For steps related to time (capturing timestamps, waiting).
71+
* `KafkaConsumerSteps.java`: For steps interacting with Kafka consumers/topics.
72+
* If your new steps cover a distinct area of functionality that doesn't fit well into any of the existing `Steps` classes above (e.g., interacting with a new external service, or a completely different domain of application logic), it's appropriate to create a new Java class for these step definitions within the `src/test/java/com/johnlpage/memex/cucumber/steps/` package. Ensure it follows the same patterns for dependency injection (e.g., `@Autowired` fields) and Spring configuration if needed.
73+
* **Reuse existing steps**: Before writing new step definition methods, check if existing ones in the relevant files can be reused.
74+
* **Create new steps**: If a step is new, add a public method annotated with `@Given`, `@When`, or `@Then` and a regex matching your Gherkin step to the appropriate class:
75+
* **Data Setup (`@Given` in `MongoPreConditionSteps.java`)**:
76+
77+
Use `MongoTemplate` for direct database interaction.
78+
Crucially, ensure any `testid` values for `VehicleInspection` data are validated using the injected `VehicleInspectionIdRangeValidator` to adhere to the configured range in `application-test.properties`.
79+
* **API Interaction (`@When`/`@Then` in `RestApiSteps.java`)**:
80+
81+
Use Rest Assured to build, send HTTP requests, and validate responses.
82+
Utilize the injected `MacrosRegister` if your Gherkin step includes URL placeholders (e.g., `<timestamp>`) that need to be dynamically replaced.
83+
84+
3. **Run Your Test**:
85+
* Execute the tests (e.g., using `mvn clean verify` or by running the specific feature/scenario from your IDE).
86+
* Ensure your new test passes and doesn't break existing tests.
87+
88+
4. **Check Code Coverage**:
89+
* After your tests pass, review the JaCoCo code coverage report (see "Code Coverage" section below) to ensure your changes are adequately covered.
90+
91+
### Code Coverage
92+
93+
We use **JaCoCo** to measure code coverage by our tests.
94+
* After running the tests (e.g., via `mvn verify` or `mvn clean test`), a code coverage report is generated.
95+
* You can find the HTML report at `target/site/jacoco/index.html`. Please review this report to ensure your contributions are adequately tested.
96+
97+
---
98+
99+
If you're planning to contribute, please familiarize yourself with these tools and ensure your changes include appropriate tests and maintain or improve code coverage.

memex/pom.xml

Lines changed: 117 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<groupId>org.springframework.boot</groupId>
77
<artifactId>spring-boot-starter-parent</artifactId>
8-
<version>3.4.1</version>
8+
<version>3.4.6</version>
99
<relativePath/> <!-- lookup parent from repository -->
1010
</parent>
1111
<groupId>com.johnlpage</groupId>
@@ -29,22 +29,43 @@
2929
<tag/>
3030
<url/>
3131
</scm>
32+
3233
<properties>
3334
<java.version>17</java.version>
3435
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
36+
<cucumber.version>7.22.2</cucumber.version>
37+
<rest-assured.version>5.5.5</rest-assured.version>
3538
</properties>
39+
40+
<dependencyManagement>
41+
<dependencies>
42+
<dependency>
43+
<groupId>io.cucumber</groupId>
44+
<artifactId>cucumber-bom</artifactId>
45+
<version>${cucumber.version}</version>
46+
<type>pom</type>
47+
<scope>import</scope>
48+
</dependency>
49+
<dependency>
50+
<groupId>io.rest-assured</groupId>
51+
<artifactId>rest-assured-bom</artifactId>
52+
<version>${rest-assured.version}</version>
53+
<type>pom</type>
54+
<scope>import</scope>
55+
</dependency>
56+
</dependencies>
57+
</dependencyManagement>
58+
3659
<dependencies>
3760

3861
<dependency>
3962
<groupId>org.springframework.boot</groupId>
4063
<artifactId>spring-boot-starter-actuator</artifactId>
4164
</dependency>
4265

43-
<!-- https://mvnrepository.com/artifact/org.springframework.kafka/spring-kafka -->
4466
<dependency>
4567
<groupId>org.springframework.kafka</groupId>
4668
<artifactId>spring-kafka</artifactId>
47-
<version>3.3.3</version>
4869
</dependency>
4970

5071
<dependency>
@@ -67,10 +88,64 @@
6788
<artifactId>spring-boot-starter-test</artifactId>
6889
<scope>test</scope>
6990
</dependency>
91+
92+
<dependency>
93+
<groupId>io.cucumber</groupId>
94+
<artifactId>cucumber-java</artifactId>
95+
<scope>test</scope>
96+
</dependency>
97+
<dependency>
98+
<groupId>io.cucumber</groupId>
99+
<artifactId>cucumber-spring</artifactId>
100+
<scope>test</scope>
101+
</dependency>
102+
<dependency>
103+
<groupId>io.cucumber</groupId>
104+
<artifactId>cucumber-junit-platform-engine</artifactId>
105+
<scope>test</scope>
106+
</dependency>
107+
108+
<dependency>
109+
<groupId>org.junit.jupiter</groupId>
110+
<artifactId>junit-jupiter</artifactId>
111+
<scope>test</scope>
112+
</dependency>
113+
<dependency>
114+
<groupId>org.junit.platform</groupId>
115+
<artifactId>junit-platform-suite</artifactId>
116+
<scope>test</scope>
117+
</dependency>
118+
119+
<dependency>
120+
<groupId>io.rest-assured</groupId>
121+
<artifactId>rest-assured</artifactId>
122+
<scope>test</scope>
123+
</dependency>
124+
<dependency>
125+
<groupId>io.rest-assured</groupId>
126+
<artifactId>json-path</artifactId>
127+
<scope>test</scope>
128+
</dependency>
129+
130+
<dependency>
131+
<groupId>org.testcontainers</groupId>
132+
<artifactId>junit-jupiter</artifactId>
133+
<scope>test</scope>
134+
</dependency>
135+
<dependency>
136+
<groupId>org.springframework.kafka</groupId>
137+
<artifactId>spring-kafka-test</artifactId>
138+
<scope>test</scope>
139+
</dependency>
140+
<dependency>
141+
<groupId>org.testcontainers</groupId>
142+
<artifactId>mongodb</artifactId>
143+
<scope>test</scope>
144+
</dependency>
145+
70146
<dependency>
71147
<groupId>org.projectlombok</groupId>
72148
<artifactId>lombok</artifactId>
73-
<version>1.18.36</version>
74149
<scope>provided</scope>
75150
</dependency>
76151
</dependencies>
@@ -85,21 +160,54 @@
85160
<plugin>
86161
<groupId>org.apache.maven.plugins</groupId>
87162
<artifactId>maven-compiler-plugin</artifactId>
88-
<version>3.13.0</version>
89163
<configuration>
90-
<source>17</source>
91-
<target>17</target>
92-
<encoding>UTF-8</encoding>
164+
<source>${java.version}</source>
165+
<target>${java.version}</target>
166+
<encoding>${project.build.sourceEncoding}</encoding>
93167
<annotationProcessorPaths>
94168
<path>
95169
<groupId>org.projectlombok</groupId>
96170
<artifactId>lombok</artifactId>
97-
<version>1.18.36</version>
98171
</path>
99172
</annotationProcessorPaths>
100173
</configuration>
101174
</plugin>
102175

176+
<plugin>
177+
<groupId>org.apache.maven.plugins</groupId>
178+
<artifactId>maven-failsafe-plugin</artifactId>
179+
<!-- TODO: Update to latest version once the bug in 3.5.3 is fixed: https://github.com/apache/maven-surefire/issues/834 -->
180+
<version>3.5.2</version>
181+
<executions>
182+
<execution>
183+
<goals>
184+
<goal>integration-test</goal>
185+
<goal>verify</goal>
186+
</goals>
187+
</execution>
188+
</executions>
189+
</plugin>
190+
191+
<plugin>
192+
<groupId>org.jacoco</groupId>
193+
<artifactId>jacoco-maven-plugin</artifactId>
194+
<executions>
195+
<execution>
196+
<id>jacoco-initialize</id>
197+
<goals>
198+
<goal>prepare-agent</goal>
199+
</goals>
200+
</execution>
201+
<execution>
202+
<id>jacoco-site</id>
203+
<phase>verify</phase>
204+
<goals>
205+
<goal>report</goal>
206+
</goals>
207+
</execution>
208+
</executions>
209+
</plugin>
210+
103211
</plugins>
104212
</build>
105213

0 commit comments

Comments
 (0)