Checkpoint Lab — Maven Testing with Surefire, Failsafe, Integration Tests, Reports, and Quality Gates
Prove a Maven project has independently verifiable unit and integration suites, reproduce the misleading result caused by omitting Failsafe verify, and restore a trustworthy gate.
Learning objectives
- Build a project with independently visible Surefire and Failsafe suites.
- Predict report/classpath/lifecycle changes before executing package and verify.
- Capture test counts and report paths as CI-consumable evidence.
- Demonstrate that omitting Failsafe verify creates a misleading green result.
- Restore failure propagation and prove the correction from isolated state.
~/.m2, global settings, production
CI, or hosted quality platform is modified.
./mvnw, grep, find, and
sha256sum. On Windows use mvnw.cmd,
Select-String, Get-ChildItem, and
Get-FileHash. Maven lifecycle semantics and report
directories are cross-platform; shell quoting/path syntax are not.
1. Checkpoint acceptance contract
The checkpoint uses the same small production classes but turns the lesson into an evidence exercise. The finished project must prove all of the following without relying on an IDE:
| Invariant | Required evidence |
|---|---|
| One Surefire unit suite |
CalculatorTest report and nonzero expected
count.
|
| One Failsafe integration suite |
HealthServiceIT report plus
failsafe-summary.xml.
|
| Package is not the integration gate | No Failsafe integration report is required before integration-test; explain lifecycle reach. |
| Verify is the integration gate | Broken IT causes nonzero exit at Failsafe verify when configured correctly. |
| Broken configuration can mislead | Same broken IT with Failsafe verify omitted demonstrates why execution alone is insufficient. |
| Clean-room proof | Final run uses an isolated local repository and no normal user cache mutation. |
2. Setup and preflight
Create a fresh disposable copy of the Lesson 2 project or rebuild it
from the snippets. Preserve all command logs under
evidence/; Maven clean removes
target/, so evidence must live outside generated build
output.
mkdir -p evidence
export LAB_REPO="$PWD/.lab-m2-checkpoint/repository"
./mvnw -v | tee evidence/toolchain.txt
./mvnw help:effective-pom -Doutput=evidence/effective-before.xml
3. Predict before execution
Prediction 1: clean package should
execute Surefire and create target/surefire-reports,
but the lifecycle has not yet reached Failsafe
integration-test/verify.
Prediction 2: with the good POM,
clean verify should create both report trees; replacing
the IT expectation with DOWN should make the final
Maven result nonzero at Failsafe verify.
Prediction 3: if failsafe:verify is
removed while the broken IT remains, integration-test can record the
failure without the normal Failsafe verification gate. That is the
deliberately misleading state you must detect from reports rather
than exit code alone.
4. Confirm the good POM and source identity
Use the Lesson 2 POM as the source of truth. Record a checksum before deliberately editing it so rollback is explicit.
sha256sum pom.xml src/main/java/dev/academy/testing/*.java src/test/java/dev/academy/testing/*.java | tee evidence/source-before.sha256
grep -n "maven-surefire-plugin\|maven-failsafe-plugin\|integration-test\|<goal>verify</goal>" pom.xml | tee evidence/plugin-bindings.txt
5. Prove package and verify are different gates
Run both from clean generated state. Preserve the logs outside
target.
set -o pipefail
./mvnw -Dmaven.repo.local="$LAB_REPO" clean package | tee evidence/package.log
find target/surefire-reports -maxdepth 1 -type f -print | sort | tee evidence/package-surefire-files.txt
find target/failsafe-reports -maxdepth 1 -type f -print 2>/dev/null | sort | tee evidence/package-failsafe-files.txt || true
./mvnw -Dmaven.repo.local="$LAB_REPO" clean verify | tee evidence/verify-good.log
find target/surefire-reports target/failsafe-reports -maxdepth 1 -type f -print | sort | tee evidence/all-report-files.txt
grep -R "tests=\|failures=\|errors=" target/surefire-reports/TEST-*.xml target/failsafe-reports/TEST-*.xml | tee evidence/test-counts.txt
6. Inject an integration failure under the correct lifecycle
Back up HealthServiceIT.java, replace its expected
value with DOWN, and run verify. Preserve
the failure log and report before changing the POM.
cp src/test/java/dev/academy/testing/HealthServiceIT.java evidence/HealthServiceIT.good.java
sed -i.bak 's/assertEquals("UP"/assertEquals("DOWN"/' src/test/java/dev/academy/testing/HealthServiceIT.java
set +e
./mvnw -Dmaven.repo.local="$LAB_REPO" clean verify > evidence/correct-gate-failure.log 2>&1
correct_status=$?
set -e
printf 'correct verify exit=%s
' "$correct_status" | tee evidence/correct-gate-exit.txt
cp target/failsafe-reports/failsafe-summary.xml evidence/failsafe-summary-correct.xml 2>/dev/null || true
verify goal makes
the Maven process fail. If the process exits 0, stop and inspect the
effective POM before continuing.
7. Reproduce the misleading state: omit Failsafe verify
Save the good POM, then replace the Failsafe execution with the
deliberately broken variant that contains
integration-test but not verify. Keep the
integration test broken. The goal is not to recommend this
configuration; it is to prove why a test report and exit status must
be interpreted together.
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.5.6</version>
<executions>
<execution>
<id>broken-it-only</id>
<goals>
<goal>integration-test</goal>
<!-- verify intentionally omitted -->
</goals>
</execution>
</executions>
</plugin>
cp pom.xml evidence/pom.good.xml
# edit only the Failsafe execution as shown above
set +e
./mvnw -Dmaven.repo.local="$LAB_REPO" clean verify > evidence/missing-verify.log 2>&1
broken_status=$?
set -e
printf 'missing-verify exit=%s
' "$broken_status" | tee evidence/missing-verify-exit.txt
find target/failsafe-reports -maxdepth 1 -type f -print | sort | tee evidence/missing-verify-report-files.txt
grep -R "failures=\|errors=" target/failsafe-reports/TEST-*.xml | tee evidence/missing-verify-counts.txt
8. Restore the correct lifecycle and prove failure propagation
Restore the POM with both Failsafe goals. Keep the test broken for one more run and prove the build fails. Then restore the good test and prove the entire checkpoint becomes green.
cp evidence/pom.good.xml pom.xml
set +e
./mvnw -Dmaven.repo.local="$LAB_REPO" clean verify > evidence/repaired-still-broken.log 2>&1
repaired_status=$?
set -e
printf 'repaired broken-test exit=%s
' "$repaired_status" | tee evidence/repaired-exit.txt
cp evidence/HealthServiceIT.good.java src/test/java/dev/academy/testing/HealthServiceIT.java
./mvnw -Dmaven.repo.local="$LAB_REPO" clean verify | tee evidence/final-green.log
sha256sum target/*.jar | tee evidence/final-artifact.sha256
9. Verification checklist
| Question | Evidence |
|---|---|
| Did the unit suite run? | Surefire XML/text + test count. |
| Did the integration suite run? | Failsafe XML/text + summary. |
| Did package and verify differ as predicted? | Preserved package/verify report file lists. |
| Did correct configuration reject the broken IT? |
correct-gate-exit.txt and Failsafe failure
report.
|
| Did missing verify demonstrate misleading failure semantics? |
missing-verify-exit.txt plus failing
integration report.
|
| Did restoration reject the broken test, then pass the good test? |
repaired-exit.txt and
final-green.log.
|
| Were normal user caches untouched? | All Maven downloads used $LAB_REPO. |
10. Cleanup / rollback
Ensure the good POM and good integration test are restored. Keep
evidence/ if you want the checkpoint record, or remove
the entire disposable project. Delete only
.lab-m2-checkpoint if reclaiming lab disk space. Do not
mutate normal user Maven state.
Knowledge check
Why preserve evidence outside target/?
mvn clean deletes generated target state. Evidence outside target survives the next controlled run and lets you compare lifecycle outcomes.
Why can a failing Failsafe integration report coexist with a misleading successful process when verify is omitted?
integration-test records failures but Failsafe is designed to defer the build-failure decision to its verify goal.
What proves a test gate more strongly than exit code alone?
Expected suite counts, report paths/content, active plugin bindings, and exit status interpreted together.
Why restore verify before restoring the broken test?
It proves the lifecycle correction itself restores failure propagation; otherwise a passing test could hide that the gate remains misconfigured.
Which Maven phase should a protected CI job normally invoke when Failsafe integration tests are required?
verify, because it reaches integration-test/post-integration-test and the Failsafe verify result check.
What production capability does Chapter 10 add?
A test-evidence operating model: explicit unit/integration stages, deterministic selection, visible reports/counts, skip/retry governance, and trustworthy failure propagation into CI.
11. Production operating model and Chapter 11 bridge
Chapter 10 adds a reliable quality-evidence boundary to the build system. Teams can now prove not merely that Maven exited successfully, but that the intended suites ran at the intended lifecycle stages, generated inspectable reports, and failed the delivery gate correctly. Chapter 11 builds on that evidence contract with Maven dependencyManagement, BOM imports, version alignment, Enforcer rules, and dependency analysis across modules.
Official references and version notes
Version-sensitive statements were checked against Apache Maven and JUnit primary documentation on 2026-08-23. The mandatory path pins Maven 3.9.16 via Maven Wrapper 3.3.4, JDK 21 to run Maven, Java 17 as the release target, JUnit 6.1.3, Maven Surefire Plugin 3.5.6, Maven Failsafe Plugin 3.5.6, Help Plugin 3.5.2, and Compiler Plugin 3.15.0.
Apache's current Surefire site advertises 3.6.0-M1. Because the version itself is a milestone identifier, these lessons treat it as version-sensitive preview/milestone material and keep the hands-on path on the final 3.5.6 line. Re-check before adopting a newer line in production.
The checkpoint intentionally keeps the integration failure harmless and local. No external service or paid test platform is required.
- Maven Surefire Plugin — Introduction
- Maven Failsafe Plugin — Introduction
- Maven Failsafe — Integration Test Goal
- Maven Failsafe — Verify Goal
- Maven Surefire — Skipping Tests
- Maven Failsafe — Skipping Tests
- Maven Failsafe — Running a Single Test
- JUnit 6.1.3 User Guide — Maven support
- JUnit 6.1.3 Release Notes
- Maven Help Plugin 3.5.2
- Maven Compiler Plugin 3.15.0
- Apache Maven Wrapper
- Maven 3.9.16 Release Notes
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.