All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mockito.internal.stubbing.defaultanswers.ForwardsInvocations Maven / Gradle / Ivy

There is a newer version: 5.12.0
Show newest version
/*
 * Copyright (c) 2007 Mockito contributors
 * This program is made available under the terms of the MIT License.
 */
package org.mockito.internal.stubbing.defaultanswers;

import static org.mockito.internal.exceptions.Reporter.delegatedMethodDoesNotExistOnDelegate;
import static org.mockito.internal.exceptions.Reporter.delegatedMethodHasWrongReturnType;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.mockito.internal.configuration.plugins.Plugins;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.plugins.MemberAccessor;
import org.mockito.stubbing.Answer;

/**
 * Internal answer to forward invocations on a real instance.
 *
 * @since 1.9.5
 */
public class ForwardsInvocations implements Answer, Serializable {
    private static final long serialVersionUID = -8343690268123254910L;

    private Object delegatedObject = null;

    public ForwardsInvocations(Object delegatedObject) {
        this.delegatedObject = delegatedObject;
    }

    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
        Method mockMethod = invocation.getMethod();

        try {
            Method delegateMethod = getDelegateMethod(mockMethod);

            if (!compatibleReturnTypes(
                    mockMethod.getReturnType(), delegateMethod.getReturnType())) {
                throw delegatedMethodHasWrongReturnType(
                        mockMethod, delegateMethod, invocation.getMock(), delegatedObject);
            }

            MemberAccessor accessor = Plugins.getMemberAccessor();
            Object[] rawArguments = invocation.getRawArguments();
            return accessor.invoke(delegateMethod, delegatedObject, rawArguments);
        } catch (NoSuchMethodException e) {
            throw delegatedMethodDoesNotExistOnDelegate(
                    mockMethod, invocation.getMock(), delegatedObject);
        } catch (InvocationTargetException e) {
            // propagate the original exception from the delegate
            throw e.getCause();
        }
    }

    private Method getDelegateMethod(Method mockMethod) throws NoSuchMethodException {
        if (mockMethod.getDeclaringClass().isAssignableFrom(delegatedObject.getClass())) {
            // Compatible class. Return original method.
            return mockMethod;
        } else {
            // Return method of delegate object with the same signature as mockMethod.
            return delegatedObject
                    .getClass()
                    .getMethod(mockMethod.getName(), mockMethod.getParameterTypes());
        }
    }

    private static boolean compatibleReturnTypes(Class superType, Class subType) {
        return superType.equals(subType) || superType.isAssignableFrom(subType);
    }
}