JacksonTestFailureExpectedInterceptor.java

package com.fasterxml.jackson.core.testutil.failure;

import java.lang.reflect.Method;

import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.InvocationInterceptor;
import org.junit.jupiter.api.extension.ReflectiveInvocationContext;

/**
 * Custom {@link InvocationInterceptor} that intercepts test method invocation.
 * To pass the test ***only if*** test fails with an exception, and fail the test otherwise.
 *
 * @since 2.19
 */
public class JacksonTestFailureExpectedInterceptor
    implements InvocationInterceptor
{
    @Override
    public void interceptTestMethod(Invocation<Void> invocation,
            ReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext)
        throws Throwable
    {
        try {
            invocation.proceed();
        } catch (Throwable t) {
            // do-nothing, we do expect an exception
            return;
        }
        handleUnexpectePassingTest(invocationContext);
    }

    /**
     * Interceptor API method that is called to intercept test template
     * method invocation, like {@link org.junit.jupiter.params.ParameterizedTest}.
     *<p>
     * Before failing passing test case, it checks if the test should pass according to the
     * {@link ExpectedPassingTestCasePredicate} provided in the
     * {@link JacksonTestFailureExpected} annotation.
     */
    @Override
    public void interceptTestTemplateMethod(Invocation<Void> invocation,
            ReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext)
        throws Throwable
    {

        try {
            invocation.proceed();
        } catch (Throwable t) {
            // do-nothing, we do expect an exception
            return;
        }

        // Before we fail the test for passing,
        // check if the test should pass according to the predicate given
        ExpectedPassingTestCasePredicate predicate = findzExpectedPassingTestCasePredicate(invocationContext);
        if (predicate != null) {
            if (predicate.shouldPass(invocationContext.getArguments())) {
                // do-nothing, we do not expect an exception
                return;
            }
        }
        handleUnexpectePassingTest(invocationContext);
    }

    private void handleUnexpectePassingTest(ReflectiveInvocationContext<Method> invocationContext) {
        // Collect information we need
        Object targetClass = invocationContext.getTargetClass();
        Object testMethod = invocationContext.getExecutable().getName();
        //List<Object> arguments = invocationContext.getArguments();

        // Create message
        String message = String.format("Test method %s.%s() passed, but should have failed", targetClass, testMethod);

        // throw exception
        throw new JacksonTestShouldFailException(message);
    }

    /**
     * @return null if the default predicate is found,
     *       otherwise instance of the {@link ExpectedPassingTestCasePredicate}.
     */
    private ExpectedPassingTestCasePredicate findzExpectedPassingTestCasePredicate(
            ReflectiveInvocationContext<Method> invocationContext)
            throws InstantiationException, IllegalAccessException
    {
        JacksonTestFailureExpected annotation = invocationContext.getExecutable().getAnnotation(JacksonTestFailureExpected.class);
        Class<? extends ExpectedPassingTestCasePredicate> predicate = annotation.expectedPassingTestCasePredicate();
        if (ExpectedPassingTestCasePredicate.class.equals(predicate)) {
            return null;
        } else {
            return predicate.newInstance();
        }
    }

}