Skip to content

Commit 1092184

Browse files
committed
GH-31 - Reference documentation for test optimizations.
1 parent d041c6d commit 1092184

File tree

2 files changed

+93
-51
lines changed

2 files changed

+93
-51
lines changed

src/docs/antora/modules/ROOT/pages/appendix.adoc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,19 @@ Can either be the class name of a custom implementation of `ApplicationModuleDet
7070
|`false`
7171
|Whether to republish outstanding event publications on restarts of the application.
7272
Usually not recommended in multi-instance deployments as other instances might still be processing events.
73+
74+
|`spring.modulith.test.file-modification-detector`
75+
|none
76+
|This can either be one of the predefined values `uncommitted-changes`, `reference-commit`, `default` or the fully-qualified class name of a `FileModificationDetector` that will be used to inspect which files of the projects have been changed.
77+
As the name suggests, `uncommitted-changes` will only consider changed files not already committed.
78+
`reference-commit` will consider all files changed since a given Git commit provided via `spring.modulith.test.reference-commit`, particularly useful CI environments in which that property could point to the commit hash of the last successful build.
79+
`default` detects all uncomitted changes and ones that have not been pushed to the current branch's tracking branch which is primarily useful for local development.
80+
81+
|`spring.modulith.test.reference-commit`
82+
|none
83+
|The commit hash of to which to calculate the set of changed files.
84+
Usually propagated in CI environments to consider all changes since the last successful build.
85+
7386
|===
7487

7588
[appendix]

src/docs/antora/modules/ROOT/pages/testing.adoc

Lines changed: 80 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
[[testing]]
22
= Integration Testing Application Modules
3+
:tabsize: 2
34

45
Spring Modulith allows to run integration tests bootstrapping individual application modules in isolation or combination with others.
56
To achieve this, place a JUnit test class in an application module package or any sub-package of that and annotate it with `@ApplicationModuleTest`:
@@ -16,7 +17,7 @@ package example.order;
1617
@ApplicationModuleTest
1718
class OrderIntegrationTests {
1819
19-
// Individual test cases go here
20+
// Individual test cases go here
2021
}
2122
----
2223
Kotlin::
@@ -28,7 +29,7 @@ package example.order
2829
@ApplicationModuleTest
2930
class OrderIntegrationTests {
3031
31-
// Individual test cases go here
32+
// Individual test cases go here
3233
}
3334
----
3435
======
@@ -39,11 +40,11 @@ If you configure the log level for `org.springframework.modulith` to `DEBUG`, yo
3940
.The log output of an application module integration test bootstrap
4041
[source, text, subs="macros"]
4142
----
42-
. ____ _ __ _ _
43+
. ____ _ __ _ _
4344
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
4445
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
4546
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
46-
' |____| .__|_| |_|_| |_\__, | / / / /
47+
' |____| .__|_| |_|_| |_\__, | / / / /
4748
=========|_|==============|___/=/_/_/_/
4849
:: Spring Boot :: (v3.0.0-SNAPSHOT)
4950
@@ -90,7 +91,7 @@ Java::
9091
@ApplicationModuleTest
9192
class InventoryIntegrationTests {
9293
93-
@MockitoBean SomeOtherComponent someOtherComponent;
94+
@MockitoBean SomeOtherComponent someOtherComponent;
9495
}
9596
----
9697
Kotlin::
@@ -100,7 +101,7 @@ Kotlin::
100101
@ApplicationModuleTest
101102
class InventoryIntegrationTests {
102103
103-
@MockitoBean SomeOtherComponent someOtherComponent
104+
@MockitoBean SomeOtherComponent someOtherComponent
104105
}
105106
----
106107
======
@@ -128,10 +129,10 @@ Java::
128129
@ApplicationModuleTest
129130
class SomeApplicationModuleTest {
130131
131-
@Test
132-
public void someModuleIntegrationTest(Scenario scenario) {
133-
// Use the Scenario API to define your integration test
134-
}
132+
@Test
133+
public void someModuleIntegrationTest(Scenario scenario) {
134+
// Use the Scenario API to define your integration test
135+
}
135136
}
136137
----
137138
Kotlin::
@@ -141,10 +142,10 @@ Kotlin::
141142
@ApplicationModuleTest
142143
class SomeApplicationModuleTest {
143144
144-
@Test
145-
fun someModuleIntegrationTest(scenario: Scenario) {
146-
// Use the Scenario API to define your integration test
147-
}
145+
@Test
146+
fun someModuleIntegrationTest(scenario: Scenario) {
147+
// Use the Scenario API to define your integration test
148+
}
148149
}
149150
----
150151
======
@@ -254,18 +255,18 @@ Java::
254255
[source, java, role="primary"]
255256
----
256257
scenario.publish(new MyApplicationEvent(…))
257-
.andWaitForEventOfType(SomeOtherEvent.class)
258-
.matching(event -> …)
259-
.toArriveAndVerify(event -> …);
258+
.andWaitForEventOfType(SomeOtherEvent.class)
259+
.matching(event -> …)
260+
.toArriveAndVerify(event -> …);
260261
----
261262
Kotlin::
262263
+
263264
[source, kotlin, role="secondary"]
264265
----
265266
scenario.publish(new MyApplicationEvent(…))
266-
.andWaitForEventOfType(SomeOtherEvent::class.java)
267-
.matching { event -> … }
268-
.toArriveAndVerify { event -> … }
267+
.andWaitForEventOfType(SomeOtherEvent::class.java)
268+
.matching { event -> … }
269+
.toArriveAndVerify { event -> … }
269270
----
270271
======
271272

@@ -280,16 +281,16 @@ Java::
280281
[source, java, role="primary"]
281282
----
282283
scenario.publish(new MyApplicationEvent(…))
283-
.andWaitForStateChange(() -> someBean.someMethod(…)))
284-
.andVerify(result -> …);
284+
.andWaitForStateChange(() -> someBean.someMethod(…)))
285+
.andVerify(result -> …);
285286
----
286287
Kotlin::
287288
+
288289
[source, kotlin, role="secondary"]
289290
----
290291
scenario.publish(MyApplicationEvent(…))
291-
.andWaitForStateChange { someBean.someMethod(…) }
292-
.andVerify { result -> … }
292+
.andWaitForStateChange { someBean.someMethod(…) }
293+
.andVerify { result -> … }
293294
----
294295
======
295296

@@ -310,20 +311,20 @@ Java::
310311
[source, java, subs="+quotes", role="primary"]
311312
----
312313
scenario.publish(new MyApplicationEvent(…))
313-
**.customize(conditionFactory -> conditionFactory.atMost(Duration.ofSeconds(2)))**
314-
.andWaitForEventOfType(SomeOtherEvent.class)
315-
.matching(event -> …)
316-
.toArriveAndVerify(event -> …);
314+
**.customize(conditionFactory -> conditionFactory.atMost(Duration.ofSeconds(2)))**
315+
.andWaitForEventOfType(SomeOtherEvent.class)
316+
.matching(event -> …)
317+
.toArriveAndVerify(event -> …);
317318
----
318319
Kotlin::
319320
+
320321
[source, kotlin, subs="+quotes", role="secondary"]
321322
----
322323
scenario.publish(MyApplicationEvent(…))
323-
**.customize { it.atMost(Duration.ofSeconds(2)) }**
324-
.andWaitForEventOfType(SomeOtherEvent::class.java)
325-
.matching { event -> … }
326-
.toArriveAndVerify { event -> … }
324+
**.customize { it.atMost(Duration.ofSeconds(2)) }**
325+
.andWaitForEventOfType(SomeOtherEvent::class.java)
326+
.matching { event -> … }
327+
.toArriveAndVerify { event -> … }
327328
----
328329
======
329330

@@ -339,18 +340,18 @@ Java::
339340
@ExtendWith(MyCustomizer.class)
340341
class MyTests {
341342
342-
@Test
343-
void myTestCase(Scenario scenario) {
344-
// scenario will be pre-customized with logic defined in MyCustomizer
345-
}
343+
@Test
344+
void myTestCase(Scenario scenario) {
345+
// scenario will be pre-customized with logic defined in MyCustomizer
346+
}
346347
347-
static class MyCustomizer implements ScenarioCustomizer {
348+
static class MyCustomizer implements ScenarioCustomizer {
348349
349-
@Override
350-
Function<ConditionFactory, ConditionFactory> getDefaultCustomizer(Method method, ApplicationContext context) {
351-
return conditionFactory -> …;
352-
}
353-
}
350+
@Override
351+
Function<ConditionFactory, ConditionFactory> getDefaultCustomizer(Method method, ApplicationContext context) {
352+
return conditionFactory -> …;
353+
}
354+
}
354355
}
355356
----
356357
Kotlin::
@@ -360,17 +361,45 @@ Kotlin::
360361
@ExtendWith(MyCustomizer::class)
361362
class MyTests {
362363
363-
@Test
364-
fun myTestCase(scenario: Scenario) {
365-
// scenario will be pre-customized with logic defined in MyCustomizer
366-
}
364+
@Test
365+
fun myTestCase(scenario: Scenario) {
366+
// scenario will be pre-customized with logic defined in MyCustomizer
367+
}
367368
368-
class MyCustomizer : ScenarioCustomizer {
369+
class MyCustomizer : ScenarioCustomizer {
369370
370-
override fun getDefaultCustomizer(method: Method, context: ApplicationContext): UnaryOperator<ConditionFactory> {
371-
return UnaryOperator { conditionFactory -> … }
372-
}
373-
}
371+
override fun getDefaultCustomizer(method: Method, context: ApplicationContext): UnaryOperator<ConditionFactory> {
372+
return UnaryOperator { conditionFactory -> … }
373+
}
374+
}
374375
}
375376
----
376377
======
378+
379+
[[change-aware-test-execution]]
380+
== Change-Aware Test Execution
381+
382+
As of version 1.3, Spring Modulith ships with a JUnit Jupiter extension that will optimize the execution of tests, so that tests not affected by changes to the project will be skipped.
383+
To enable that optimization, declare the `spring-modulith-junit` artifact as a dependency in test scope:
384+
385+
[source, xml]
386+
----
387+
<dependency>
388+
<groupId>org.springframework.modulith</groupId>
389+
<artifactId>spring-modulith-junit</artifactId>
390+
<scope>test</scope>
391+
</dependency>
392+
----
393+
394+
Tests will be selected for execution if they reside in either a root module, a module that has seen a change or one that transitively depends on one that has seen a change.
395+
The optimization will back off optimizing the execution under the following circumstances:
396+
397+
* The test execution originates from an IDE as we assume the execution is triggered explicitly.
398+
* The set of changes contains a change to a resource related to a build system (`pom.xml`, `build.gradle`, `gradle.properties`).
399+
* The set of changes contains a change to any classpath resource.
400+
* The project does not contain a change at all (typical in a CI build).
401+
402+
To optimize the execution in a CI environment, you need to populate the xref:appendix.adoc#configuration-properties[`spring.modulith.test.reference-commit` property] pointing to the commit of the last successful build and make sure that the build checks out all commits up to the reference one.
403+
The algorithm detecting changes to application modules will then consider all files changed in that delta.
404+
To override the project modification detection, declare an implementation of `FileModificationDetector` via the xref:appendix.adoc#configuration-properties[`spring.modulith.test.file-modification-detector` property].
405+

0 commit comments

Comments
 (0)