|
| 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. |
0 commit comments