Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add how to deal with dependencies in scenarios #215

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -1 +1 @@
spring.modulith.events.jdbc.schema-initialization.enabled=true
spring.modulith.events.jdbc.schema-initialization.enabled=true
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>

<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />

<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>

<logger name="org.springframework.modulith" level="WARN"/>
<logger name="com.github.dockerjava" level="WARN" />
<logger name="example" level="INFO" />

</configuration>
89 changes: 89 additions & 0 deletions src/docs/asciidoc/30-testing.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down