diff --git a/spring-modulith-examples/spring-modulith-example-epr-jdbc/src/main/resources/application.properties b/spring-modulith-examples/spring-modulith-example-epr-jdbc/src/main/resources/application.properties index 79d64cb28..cff8df0fd 100644 --- a/spring-modulith-examples/spring-modulith-example-epr-jdbc/src/main/resources/application.properties +++ b/spring-modulith-examples/spring-modulith-example-epr-jdbc/src/main/resources/application.properties @@ -1 +1 @@ -spring.modulith.events.jdbc.schema-initialization.enabled=true +spring.modulith.events.jdbc.schema-initialization.enabled=true \ No newline at end of file diff --git a/spring-modulith-examples/spring-modulith-example-epr-jdbc/src/test/resources/logback.xml b/spring-modulith-examples/spring-modulith-example-epr-jdbc/src/test/resources/logback.xml new file mode 100644 index 000000000..e2eb0089c --- /dev/null +++ b/spring-modulith-examples/spring-modulith-example-epr-jdbc/src/test/resources/logback.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/docs/asciidoc/30-testing.adoc b/src/docs/asciidoc/30-testing.adoc index b548f0423..796091fa8 100644 --- a/src/docs/asciidoc/30-testing.adoc +++ b/src/docs/asciidoc/30-testing.adoc @@ -178,6 +178,95 @@ The `result` handed into the `….andVerify(…)` method will be the value retur By default, non-`null` values and non-empty ``Optional``s will be considered a conclusive state change. This can be tweaked by using the `….andWaitForStateChange(…, Predicate)` overload. +[[testing.scenarios.of.event-driven-modules]] +=== Integration Test Scenarios of Event-Driven Modules + +Consider the following scenario: `order` module publishes an event which is consumed by `inventory` module which in its turn sends another event: + +[source, java] +---- +@Service +@RequiredArgsConstructor +public class OrderManagement { + + private final @NonNull ApplicationEventPublisher events; + + @Transactional + public void complete(Order order) { + // ... + events.publishEvent(new OrderCompleted(order.getId())); + } +} +---- + +[source, java] +---- +@Service +@RequiredArgsConstructor +class InventoryManagement { + + private final ApplicationEventPublisher events; + + @ApplicationModuleListener + void on(OrderCompleted event) { + // ... + events.publishEvent(new InventoryUpdated(orderId)); + } +} + +---- + +If we were to write an integration test which checks that `InventoryUpdated` event is fired after `InventoryManagement` receives `OrderCompleted` in `order` test package, it could look as follows: + +[source, java] +---- +@ApplicationModuleTest(extraIncludes = "inventory") +@RequiredArgsConstructor +class OrderManagementTests { + + @Test + void inventoryUpdatedIsPublishedOnOrderCompleted(Scenario scenario) { + Order order = new Order(); + + scenario.publish(new OrderCompleted(order.getId())) + .andWaitForEventOfType(InventoryUpdated.class) + .toArrive(); + } +} + +---- + +Since `order` doesn't have a direct dependency on `inventory` module using `DIRECT_DEPENDENCIES` or `ALL_DEPENDENCIES` bootstrap modes will not import `inventory` module related logic. As a result `InventoryUpdated` event will not be fired and the test will fail. +In such cases `ApplicationModuleTest` annotation provides `extraIncludes` parameter where extra dependencies may be specified. + +One could argue that the above test doesn't really belong to `order` test package but rather it's an application-wide integration test. It could probably be placed directly under the root test package: + +[source, java] +---- +@SpringBootTest +@EnableScenarios +class ApplicationIntegrationTests { + + @Test + void inventoryUpdatedIsPublishedOnOrderCompleted(Scenario scenario) { + Order order = new Order(); + + scenario.publish(new OrderCompleted(order.getId())) + .andWaitForEventOfType(InventoryUpdated.class) + .toArrive(); + } +} + +---- + +In such case the combination of `@SpringBootTest` and `@EnableScenarios` can be used (specifying `ApplicationModuleTest` will result in error since the root package is not part of any module). + +To summarize when writing integration tests for application modules which interact via events instead of directly depending on another module (via Spring dependency injection mechanisms) Spring Modulith provides +two options to include dependencies: + +* Specifying the dependencies in `extraIncludes` parameter of `@ApplicationModuleTest` +* Using the combination of `@SpringBootTest` and `@EnableScenarios` + [[testing.scenarios.customize]] === Customizing Scenario Execution