Chapter 10Lesson 03~140 minutes

Maven Testing with Surefire, Failsafe, Integration Tests, Reports, and Quality Gates: Configuration, Design Choices, and Tradeoffs

Choose deliberately among test-stage separation, naming versus explicit includes, fast feedback versus broader coverage, and retry/failure policies that preserve trustworthy quality gates.

Test StrategyNamingFast FeedbackRetriesTradeoffs

Learning objectives

  • Choose when unit and integration tests deserve separate lifecycle stages.
  • Compare naming conventions with explicit includes/excludes as governance mechanisms.
  • Balance fast developer feedback against complete CI verification.
  • Evaluate fail-fast, ignore, retry, and quarantine patterns by the evidence they preserve or hide.
  • Separate Maven test configuration from external CI/reporting/product policy.
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.

1. Design from risk and evidence, not vocabulary

The words “unit” and “integration” are useful only if they change operational behavior. A suite that needs a database, service process, filesystem fixture, or long-lived resource often benefits from the Failsafe lifecycle because setup/teardown and delayed failure are meaningful. A fast deterministic pure-Java test benefits from the earlier Surefire stage. The boundary should be reviewable in the POM and observable in reports.

2. One test phase versus unit/integration separation

Approach Advantages Costs / risk Good fit
Everything under Surefire Simple command and one report tree. Slow/external tests fail early; cleanup semantics may be poor; fast feedback degrades. Small project where all tests are truly fast and isolated.
Surefire + Failsafe Distinct timing, report paths, failure semantics, CI stages. More configuration and naming discipline. Service/application builds with meaningful integration risk.
Separate modules/suites beyond Failsafe Strong ownership/environment isolation. More reactor/CI complexity. Large systems with expensive or independently deployed integration environments.

3. Naming convention versus explicit includes

Conventions are easy to understand and portable: *Test versus *IT makes the intended runner visible in the filename. Explicit <includes> are useful when legacy naming cannot change or a suite is intentionally partitioned, but they add another hidden selection layer. Whichever mechanism you choose, CI should prove expected test counts so a naming typo cannot silently remove coverage.

4. Fast feedback versus broad coverage

Developer loops optimize latency; delivery gates optimize confidence. It is reasonable for a developer to use -Dtest=... while editing one class. It is not reasonable for that filtered command to become the only protected-branch gate. A common pattern is fast unit verification on every change plus broader verify evidence before promotion.

Context Recommended scope Evidence expectation
Local edit loop Targeted Surefire test or unit suite. Immediate console + focused report.
Pull request / pre-merge All unit tests; integration tests according to risk and runtime. Published XML, counts, nonzero failure propagation.
Release/promotion gate Full required Maven verify contract. Unit + integration evidence tied to the exact source/artifact build.

5. Fail-fast, ignore, and retry are governance choices

Fail-fast can reduce wasted compute after enough evidence exists, but it may hide the full defect set. Failure ignore keeps the build moving and therefore weakens the gate unless a later explicit policy rejects the result. Retries can reduce transient noise, but Surefire/Failsafe rerunFailingTestsCount records rerun attempts and a passing retry should still be treated as flakiness evidence rather than proof that the original failure did not matter.

Production pattern: do not normalize permanent testFailureIgnore=true. If a flaky test is temporarily quarantined, make ownership, expiry, and visible reporting explicit.

6. Empty suites need an intentional policy

Surefire/Failsafe can be configured with failIfNoTests. Whether every module must have tests is an architecture decision, but a CI job that claims to verify a suite should not quietly run zero tests because a filename changed. Use explicit expectations: report presence, minimum/known counts, or module-specific policy.

7. Keep Maven, JDK, CI, and external quality tools in their lanes

Maven controls lifecycle-bound test execution and reports. JUnit supplies the test engine/API. The JDK runs Maven and forked test JVMs. CI schedules jobs and ingests reports. A quality platform may add coverage/static-analysis policy. An IDE is a developer interface. A test passing in the IDE does not prove the Maven lifecycle or CI report contract is correct.

8. Worked decision: API service with 90-second database checks

A team has 800 sub-second pure unit tests and 40 database integration tests taking about 90 seconds. Running every test under Surefire makes local test slow and can interrupt environment cleanup on failure. A better design is Surefire for unit tests and Failsafe for *IT database checks, with the protected branch requiring verify. Developers can filter either stage while debugging, but the gate remains unfiltered.

Decision Reason Observable proof
Surefire for *Test Fast feedback. Surefire report count matches expected unit suite.
Failsafe for *IT Later lifecycle and teardown-friendly failure semantics. Failsafe reports + summary + verify result.
No permanent failure ignore Gate must reject defects. Nonzero Maven exit when required test fails.
Retry count remains zero by default Do not hide nondeterminism. A flaky failure remains visible until deliberately governed.

Knowledge check

When is explicit includes configuration preferable to naming conventions?

Why can filtered local commands coexist with an unfiltered CI gate?

What is the main risk of testFailureIgnore?

Do retries erase the original failure?

Which layer decides whether an uploaded XML report blocks deployment?

9. Summary and bridge

Test architecture is a set of explicit tradeoffs around lifecycle stage, selection, runtime, and evidence. Lesson 4 uses those decisions to diagnose the most dangerous false-green states: the wrong runner, omitted verify, hidden skips, missing reports, and retries that normalize defects.

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.