Chapter 10Lesson 01~135 minutes

Maven Testing with Surefire, Failsafe, Integration Tests, Reports, and Quality Gates: Concepts, Architecture, and Mental Model

Separate Maven unit-test and integration-test verification into explicit lifecycle stages, report locations, naming rules, skip semantics, and CI-consumable failure evidence.

SurefireFailsafeUnit TestsIntegration TestsQuality Evidence

Learning objectives

  • Explain why Surefire and Failsafe serve different lifecycle roles instead of treating all tests as one command.
  • Predict which names are selected as unit tests versus integration tests and where each report appears.
  • Distinguish the test, integration-test, post-integration-test, and verify failure boundaries.
  • Separate test execution, test compilation, report evidence, and CI gate decisions.
  • Inspect the effective test model before changing configuration.
Current baseline — verified 2026-08-23. Labs use Maven 3.9.16 via Maven Wrapper 3.3.4, JDK 21 to run Maven, Java 17 as the compiler release target, JUnit 6.1.3, Surefire 3.5.6, Failsafe 3.5.6, Help Plugin 3.5.2, and Compiler Plugin 3.15.0. JUnit 6 requires Java 17 or newer. All Maven resolution uses a disposable project-local repository; no normal ~/.m2, global settings, production CI, or hosted quality platform is modified.
Version note: the Apache Surefire site currently exposes 3.6.0-M1 as its current release documentation. The M1 suffix is a milestone identifier. This chapter deliberately pins 3.5.6 for a conservative, final-version lab baseline and calls out behavior from the current docs only where it matters. Do not silently change plugin versions in CI without reviewing release notes and rerunning the evidence checks.
Cross-platform note: POSIX examples use ./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. The practical problem: a green command can still be a weak gate

Chapter 09 made reactor scope explicit. The next production question is whether every selected module is actually verified at the right risk boundary. A fast unit test and a slower integration test may both be Java methods, but they have different lifecycle needs. If Maven runs both under the same early phase, teardown can be skipped on failure, CI may wait too long for fast feedback, or a report collector may mistake an absent suite for a passing suite.

The important model is therefore not “Maven runs tests.” It is declared test classes → test-selection rules → lifecycle-bound plugin goal → JVM execution → report files → plugin failure state → Maven process exit code → CI gate.

Unit and integration evidence flow
flowchart TD
  A[src/test/java] --> B[Surefire test goal]
  A --> C[Failsafe integration-test goal]
  B --> D[target/surefire-reports]
  C --> E[target/failsafe-reports]
  D --> F[Maven test-phase result]
  E --> G[Failsafe summary]
  G --> H[Failsafe verify]
  F --> I[CI gate]
  H --> I

2. Surefire versus Failsafe: same test classpath, different lifecycle contract

Surefire is the Maven plugin used in the test phase for unit tests. A failing Surefire test normally fails that phase immediately. Failsafe is designed for integration tests: its integration-test goal executes tests and records the outcome, while its separate verify goal checks the summary and turns failed integration tests into a build failure. That separation lets post-integration-test cleanup run before Maven rejects the build.

Layer Typical purpose Default evidence
Surefire test Fast unit/component tests during Maven test. target/surefire-reports/TEST-*.xml plus text reports.
Failsafe integration-test Execute integration-style tests later in the lifecycle. target/failsafe-reports/TEST-*.xml plus failsafe-summary.xml.
Failsafe verify Evaluate Failsafe summary after teardown opportunity. Maven success/failure for integration-test evidence.
CI report ingestion Display/aggregate evidence; not the test runner itself. Uploaded XML plus process exit status and explicit test counts.

3. Naming is executable selection policy

Surefire and Failsafe scan compiled test classes using different conventional name patterns. For a beginner, this is easiest to treat like a routing table: a class name decides which runner sees it unless you override includes/excludes. Surefire commonly selects names such as Test*, *Test, *Tests, and *TestCase. Failsafe commonly selects IT*, *IT, and *ITCase. A class named PaymentTest that is operationally an integration test is still likely to run under Surefire unless you change the model.

Class Conventional runner Risk if the name is wrong
CalculatorTest Surefire Appropriate for a fast unit test.
HealthServiceIT Failsafe Appropriate for an integration-stage test.
DatabaseTest intended as an integration test Surefire by convention Slow/external test runs too early and may abort lifecycle cleanup.

4. Lifecycle position controls when evidence becomes authoritative

Running mvn package reaches test and packaging, but it stops before integration-test and verify. A build can therefore have a valid JAR and unit-test reports while having no integration-test evidence yet. Running mvn verify reaches the integration-test sequence and lets Failsafe perform its final result check.

./mvnw -v
./mvnw help:effective-pom -Doutput=evidence/effective-pom.xml
./mvnw surefire:help -Ddetail=true -Dgoal=test
./mvnw failsafe:help -Ddetail=true -Dgoal=integration-test
./mvnw failsafe:help -Ddetail=true -Dgoal=verify

5. “Skipped” and “not compiled” are different states

-DskipTests is intended to skip executing tests while test compilation can still occur. -Dmaven.test.skip=true is broader: the Compiler, Surefire, and Failsafe plugins honor it, so test compilation can be skipped too. Failsafe also supports -DskipITs for integration tests specifically. These flags are operational exceptions, not proof of quality.

Gate rule: a zero-test or skipped-test run is not “verified” merely because Maven exits 0. CI should preserve the skip reason, expected suite count, report presence, and policy owner.

6. Quality gates are evidence contracts, not product logos

For this chapter, a quality gate means a local, explainable rule such as “the expected unit and integration suites ran, their reports exist, failures propagated, and the Maven process exited successfully.” Coverage services, SonarQube, browser automation, and load testing have dedicated courses. Here the build-tool responsibility is to produce and fail on trustworthy evidence that those systems can consume.

7. DevOps operating contract

A delivery pipeline should be able to answer five questions without opening an IDE: Which test stage ran? Which test classes were selected? How many tests ran? Where are the machine-readable reports? What exact lifecycle goal caused a failing test to become a failing build? If any answer is ambiguous, the CI gate is weaker than it appears.

8. Read-only mini-lab

Before authoring tests, inspect an existing project. Do not change the POM yet. Record wrapper/JDK identity, effective plugin configuration, whether Surefire/Failsafe are active, and whether old report directories already exist. Generated reports are evidence from a previous execution, not proof that the current source state was tested.

mkdir -p evidence
./mvnw -v | tee evidence/toolchain.txt
./mvnw help:effective-pom -Doutput=evidence/effective-pom.xml
find target -maxdepth 2 -type f 2>/dev/null | sort | tee evidence/preexisting-files.txt || true

Knowledge check

Why does Failsafe split integration-test execution from verify?

Does a successful mvn package prove integration tests passed?

What does -Dmaven.test.skip=true change beyond -DskipTests?

Why is a zero-test build not automatically a trustworthy green gate?

Which report directory normally belongs to Failsafe?

9. Summary and bridge

Surefire gives the test phase fast unit evidence. Failsafe gives integration tests a later execution point plus a separate verify failure boundary. Naming, skip flags, report files, and exit codes are all part of the model. Lesson 2 turns that model into a disposable project whose reports and failures you can inspect directly.

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.

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.