@Documented
@Incubating
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@ExtendWith(ExpectedToFailExtension.class)
public @interface ExpectedToFail
Inverts the pass/fail outcome of the annotated test method (or every test method on the annotated class): a test that throws is reported as passing, and a test that completes normally is reported as failing.
Useful for asserting that some condition reliably fails, and for testing test-infrastructure code that should propagate failures.
Optionally constrain the expected failure by exception type or by a closure predicate evaluated against the thrown exception:
@Test
@ExpectedToFail(IllegalStateException)
void mustThrowIse() { throw new IllegalStateException("boom") }
@Test
@ExpectedToFail({ ex instanceof RuntimeException && message.contains('boom') })
void mustMatchPredicate() { throw new RuntimeException("kaboom!") }
Closure predicates are evaluated with three bindings: ex (the
thrown exception), message (its message, possibly null),
and cause (its cause, possibly null). The test is
reported as passing iff the predicate returns a Groovy-truthy value.
For callers who want compile-time enforcement that the configured class
is a Throwable subclass, set exception() instead of
(or alongside) value(). exception and a closure
value compose: the type acts as a guard that runs before the
predicate, so the closure body can drop the ex instanceof X
boilerplate. exception and a Throwable value are
mutually exclusive (they would specify the type twice).
TestAbortedException (the exception thrown by JUnit
Assumptions) is never treated as the expected failure; it's
always rethrown so the test is reported as aborted.
Composition with ForkedJvm: works in either declaration
order via explicit coordination between the two extensions (no reliance on
annotation iteration order). When @ExpectedToFail is declared
before @ForkedJvm (i.e., outer), the inversion happens in
the parent JVM after the failure propagates from the fork — exercising
@ForkedJvm's serialization. When declared after (inner), the
inversion happens inside the forked child.
| Type | Name and Description |
|---|---|
Class<? extends Throwable> |
exceptionType-safe alternative to value(): declares the expected exception type with compile-time enforcement that it is a Throwable subclass. |
String |
reasonOptional human-readable explanation of why this test is expected to fail. |
Class<?> |
valueEither an expected Throwable subclass or a Closure
predicate evaluated against the thrown exception. |
Type-safe alternative to value(): declares the expected exception type with compile-time enforcement that it is a Throwable subclass. Defaults to Throwable, which matches anything.
Mutually exclusive with a Throwable value(); composes with a closure value() as a type guard evaluated before the predicate.
Optional human-readable explanation of why this test is expected to fail. Surfaced in the failure message when the test unexpectedly passes.
Either an expected Throwable subclass or a Closure
predicate evaluated against the thrown exception. Defaults to
Throwable, which matches anything.
When set to a Throwable subclass, mutually exclusive with exception(). When set to a closure, composes with exception(): the type guard is checked first, then the closure predicate.