Java 8+Maven环境下如何标记非强制测试,构建时允许其失败且不忽略
Great question! I’ve faced similar requirements before, and while there’s no out-of-the-box @FailureWarningOnly annotation in JUnit, we can build exactly what you need using a combination of JUnit features and Maven Surefire configuration. Let’s break down two solid approaches that meet all your criteria:
Approach 1: JUnit Custom Rule with Annotation
This lets you mark individual tests as non-mandatory, keep their failure logs/stack traces, and only block the build if mandatory tests fail.
Step 1: Create a Custom Annotation
First, define an annotation to flag non-mandatory tests:
import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface NonMandatoryTest { }
Step 2: Build a Test Rule to Handle Failures
Create a JUnit TestRule that catches failures for annotated tests, logs them as warnings, and doesn’t propagate the exception (so the build doesn’t fail):
import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; public class NonMandatoryTestRule implements TestRule { @Override public Statement apply(Statement base, Description description) { return new Statement() { @Override public void evaluate() throws Throwable { try { base.evaluate(); } catch (Throwable failure) { if (description.getAnnotation(NonMandatoryTest.class) != null) { // Log the failure with stack trace without blocking the build System.err.printf("⚠️ NON-MANDATORY TEST FAILED: %s%n", description.getDisplayName()); failure.printStackTrace(); } else { // Re-throw for mandatory tests to block the build throw failure; } } } }; } }
Step 3: Use the Rule in Your Tests
Add the rule to your test classes and mark non-mandatory tests with the annotation:
import org.junit.Rule; import org.junit.Test; import org.springframework.boot.test.context.SpringBootTest; import static org.mockito.Mockito.*; @SpringBootTest public class OrderServiceTest { @Rule public NonMandatoryTestRule nonMandatoryRule = new NonMandatoryTestRule(); // Mandatory test: failure blocks build @Test public void testCreateOrder_ValidRequest() { // Your test logic with Mockito mocks verify(mockOrderRepository).save(any(Order.class)); } // Non-mandatory test: failure logs warning, build continues @NonMandatoryTest @Test public void testCreateOrder_LowStockEdgeCase() { // Flaky or non-critical test logic assert false; // This will log a warning but not fail the build } }
Approach 2: Maven Surefire Split Execution
This approach splits your tests into mandatory and non-mandatory groups using JUnit’s @Category annotation, then configures Maven to fail only if mandatory tests fail. Non-mandatory test failures are logged but don’t block the build.
Step 1: Create a Category Marker Interface
Define an interface to mark non-mandatory test classes/methods:
public interface NonMandatoryTests { }
Step 2: Mark Tests with the Category
Tag your non-mandatory tests with the category:
import org.junit.Test; import org.junit.experimental.categories.Category; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class PaymentServiceTest { // Mandatory test @Test public void testProcessPayment_Success() { // Critical test logic } // Non-mandatory test @Category(NonMandatoryTests.class) @Test public void testProcessPayment_ThirdPartyGatewayTimeout() { // Edge case or flaky external integration test assert false; // Failure is logged but build continues } }
Step 3: Configure Maven Surefire Plugin
Update your pom.xml to run tests in two phases:
- First phase: Run only mandatory tests, fail the build if any fail.
- Second phase: Run only non-mandatory tests, ignore failures but keep logs.
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.2</version> <!-- Compatible with Java 8 --> <executions> <!-- Execute mandatory tests (fail on failure) --> <execution> <id>run-mandatory-tests</id> <goals> <goal>test</goal> </goals> <configuration> <groups>!com.yourpackage.NonMandatoryTests</groups> <testFailureIgnore>false</testFailureIgnore> </configuration> </execution> <!-- Execute non-mandatory tests (ignore failures) --> <execution> <id>run-non-mandatory-tests</id> <goals> <goal>test</goal> </goals> <configuration> <groups>com.yourpackage.NonMandatoryTests</groups> <testFailureIgnore>true</testFailureIgnore> <includes> <include>**/*Test.java</include> </includes> </configuration> </execution> </executions> </plugin> </plugins> </build>
Key Benefits of Both Approaches
- No skipped tests: All tests run, so you get full failure details including stack traces.
- Mandatory test enforcement: Only non-mandatory test failures are allowed; critical failures still block the build.
- Compatible with your stack: Works seamlessly with Java 8, Spring, Mockito, and JUnit.
- Retained failure info: You get full logs to debug non-mandatory test failures later.
Which to Choose?
- Use the Custom Rule if you want fine-grained control per test method and prefer keeping all tests in a single execution.
- Use the Surefire Split Execution if you want clear separation between test groups and need separate test reports for mandatory/non-mandatory tests.
内容的提问来源于stack exchange,提问作者jpmottin

