Both ModuleContextCustomizer and ModuleTypeExcludeFilter contribute to the calculation of the context configuration which the Spring Test Context Framework uses to decide whether it's necessary to create new ApplicationContext instances.
Both of them previously used a Supplier<ModuleTestExecution> to calculate equals(…) and hashCode() which -- by definition -- does not result in the same result even when created with identical input. We now rather use the source class instance eventually backing the ModuleTestExecution, as that is the internal cache key in turn.
Previously, we had a variety of places calculating the overall packages to inspect for types. This is now all consolidated into the ModulithMetadata abstraction exposing them directly.
If Spring Modulith packages were explicitly configured as autoconfiguration or entity scan packages, the test autoconfiguration would fail as it previously attempted to manipulate an immutable list. We now create a copy of that list to fix this.
We now create artificial root application modules for all root packages to detect violations (for example, types located in root packages referring to module-internal types).
@ApplicationModuleTest is now meta-annotated with @SpringBootTest. This allows us to remove a couple of declarations that we actually had copied from it (such as the TestContextBootstrapper, the SpringExtension etc.)
The presence of the original annotation allow test-related auto-configuration to inspect @SprignBootTest for particular configuration. For example, we now alias the WebEnvironment to make it configurable for the test execution.
We now register a default customizer on the Scenario instances created to pick up an ExecutorService defined in the ApplicationContext so that customizations made to that are considered in the test execution.
We now avoid accessing the ApplicationContext from a BeforeAllCallback as this might cause the context initialization before other extensions had time to kick in.
The dedicated annotation allows using Scenario as test parameter in @SpringBootTest-based integration tests, too.
ScenarioParameterResolver now throws a better error message if a TransactionTemplate bean is missing from the ApplicationContext.
We now provide a ScenarioCustomizer that can be used to prepare Scenario instances for test methods with a common customizer. This avoids the need to call ….customize(…) for all Scenarios declared in a test class with the same logic.
We now use Spring Test Context's TestContextAnnotationUtils to lookup the @ApplicationModuleTest annotation to eventually bootstrap an ApplicationModules instance. That ensures that we find the annotation on JUnit 5's @Nested classes.
To make sure that we trigger transactional event listeners from Scenario based integration tests, we trigger stimulus executions using transactions configured to require a new transaction. This makes sure that Scenarios work in integration tests using @Transactional, of course circumventing the default rollback behavior of those transactions. There's no other way to implement this as we *need* to commit a transaction to trigger the delivery of transaction-bound events.
Fixed a bug in the handling of Scenario.stimulus(Supplier<T>) as for this, the Supplier handed into the method had not been executed transactionally at all.
Move the registration of cleanup callbacks to the intermediate When type to avoid overloads of Scenario.stimulate(…). Also, to prevent multiple lambda-style parameters for ….stimulate(…) as they don't distinguish nice on the declaration side. Removed the varargs from ….publish(…) methods and wait for the actual request to add support for preparing, in transaction callbacks (potentially to be introduced as ….prepare(…) method).
The default acceptance criteria for state change expectations now also considers a boolean true as concluding method result.
The ApplicationListener we deploy to capture events published during a test method execution now uses an InheritableThreadLocal so that events published on threads spawned from the main test execution thread also end up in the PublishedEvents instance prepared for the test method.
Also, the registration of that particular event listener avoids duplicate registrations by inspecting the application context in use for the test method execution for an already registered listener, falling back to registering one.
We now optionally integrate with the JGraphT library to calculate the topological order of modules based on their dependency structure. That order is then exposed in ApplicationModules' iteration and via ….getComparator().
Renamed FormattableJavaClass to FormattableType and allow it to be created from a plain Class as well.
In case AssertJ is on the classpath, test cases using the PublishedEventsExtension (for example, implicitly activated via @ApplicationModuleTest) can get an AssertablePublishedEvents injected into the method. In contrast to the original PublishedEvents, that one acts as AssertProvider so that it can be used in assertThat(…).… expressions to verify application events published.
Polished API in PublishedEvents for a more consistent experience.
Introduce Spring Modulith Actuator module to expose the application module structure as Spring Boot actuator. This required a Spring Modulith Runtime module to be extracted from the Spring Modulith Observability one. The former now contains the auto-configured ApplicationRuntime and ApplicationModulesRuntime bean instances to be able to bootstrap a ApplicationModules asynchronously on application startup. The actuator module then contains a Spring Boot @Endpoint implementation consuming the ApplicationModules to render JSON describing the application module structure.
Moved TestUtils from spring-modulith-test to spring-modulith-integration-test as it's only used there and doesn't need to be exposed to user applications.
Adapt to API changes in Micrometer and Spring Boot's test context loader API now exposing a checked ContextLoadException to signal failures during ApplicationContext bootstraps.
Overhauled the module canvas in various ways. By default, we now skip "empty" rows. In other words, if we do not find any published events for example, we skip the entire row. CanvasOptions.revealEmptyRows() can be used to keep those empty rows around. We now also expose all value types and Spring bean references.
Refactored the dependency lookup APIs on ApplicationModule. Both DependencyType and DependencyDepth are now top-level types. Also, ApplicationModuleDependency and ApplicationModuleDependencies were introduced as explicit types. In the course of that, ApplicationModule.getDependencies(…) has got its return type changed from List<ApplicationModule> to ApplicationModuleDependencies. The internal ModuleDependency type has been renamed to QualifiedDependency.
Also, keep autoconfiguration packages as String array to avoid ClassCastExceptions when the bean definition's constructor arguments are inspected manually (like in AutoConfigurationPackages.addBasePackages(…).