quarkusio/quarkus · error · IllegalStateException

A test method annotated with @TestReactiveTransaction must a

Error message

A test method annotated with @TestReactiveTransaction must accept io.quarkus.test.vertx.UniAsserter

What it means

Methods annotated with @TestReactiveTransaction run under a reactive transaction and must use the UniAsserter callback style of Quarkus reactive testing; the interceptor detects whether the method has a UniAsserter parameter. If it does not, there is no way to drive the reactive chain, so the interceptor throws immediately at call time.

Source

Thrown at extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/TestReactiveTransactionalInterceptor.java:26

import jakarta.interceptor.AroundInvoke;
import jakarta.interceptor.InvocationContext;

import io.smallrye.mutiny.Uni;

public class TestReactiveTransactionalInterceptor {

    private static final String JUNIT_TEST_ANN = "org.junit.jupiter.api.Test";
    private static final String JUNIT_BEFORE_EACH_ANN = "org.junit.jupiter.api.BeforeEach";
    private static final String JUNIT_AFTER_EACH_ANN = "org.junit.jupiter.api.AfterEach";
    private static final String UNI_ASSERTER_CLASS = "io.quarkus.test.vertx.UniAsserter";

    @AroundInvoke
    public Object intercept(InvocationContext context) throws Exception {
        if (isSpecialTestMethod(context)) {
            return handleSpecialTestMethod(context);
        }
        // TODO validate this requirement during build
        throw new IllegalStateException(
                "A test method annotated with @TestReactiveTransaction must accept io.quarkus.test.vertx.UniAsserter");
    }

    protected boolean isSpecialTestMethod(InvocationContext ic) {
        Method method = ic.getMethod();
        return hasParameter(UNI_ASSERTER_CLASS, method)
                && (hasAnnotation(JUNIT_TEST_ANN, method)
                        || hasAnnotation(JUNIT_BEFORE_EACH_ANN, method)
                        || hasAnnotation(JUNIT_AFTER_EACH_ANN, method));
    }

    protected Object handleSpecialTestMethod(InvocationContext ic) {
        // let's not deal with generics/erasure
        Class<?>[] parameterTypes = ic.getMethod().getParameterTypes();
        Object uniAsserter = null;
        Class<?> uniAsserterClass = null;
        for (int i = 0; i < parameterTypes.length; i++) {
            Class<?> klass = parameterTypes[i];

View on GitHub (pinned to e1c734241f)

Solutions

  1. Add an io.quarkus.test.vertx.UniAsserter parameter to the test method and use asserter.assertThat/Uni assertions
  2. Remove @TestReactiveTransaction if the test does not need a reactive transaction
  3. Use plain @TestReactiveTransaction with UniAsserter, or switch to inject + manually chaining Uni for non-test code

Example fix

// before
@TestReactiveTransaction
void testSave() {
    entity.persist();
}
// after
@TestReactiveTransaction
void testSave(UniAsserter asserter) {
    asserter.assertThat(() -> entity.persist().chain(() -> entity.count()), count -> count == 1);
}
Defensive patterns

Strategy: validation

Validate before calling

if (!java.util.Arrays.stream(testMethod.getParameterTypes()).anyMatch(p -> p == io.quarkus.test.vertx.UniAsserter.class)) {
    throw new AssertionError("@TestReactiveTransaction test must accept UniAsserter");
}

Prevention

When it happens

Trigger: A test method annotated with @TestReactiveTransaction is invoked by the interceptor and its Method has no io.quarkus.test.vertx.UniAsserter parameter (isSpecialTestMethod returns false).

Common situations: Writing a plain void/synchronous test method and annotating it with @TestReactiveTransaction expecting declarative transaction semantics; copy-pasting @Transactional test style into a reactive test; renaming/refactoring that removed the UniAsserter parameter.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/ec6ae739d6a60f12. Report an issue: GitHub.