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

org.mockito.internal.handler.MockHandlerImpl 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.handler;

import static org.mockito.internal.listeners.StubbingLookupNotifier.notifyStubbedAnswerLookup;
import static org.mockito.internal.progress.ThreadSafeMockingProgress.mockingProgress;

import org.mockito.internal.creation.settings.CreationSettings;
import org.mockito.internal.invocation.InvocationMatcher;
import org.mockito.internal.invocation.MatchersBinder;
import org.mockito.internal.stubbing.InvocationContainerImpl;
import org.mockito.internal.stubbing.OngoingStubbingImpl;
import org.mockito.internal.stubbing.StubbedInvocationMatcher;
import org.mockito.internal.stubbing.answers.DefaultAnswerValidator;
import org.mockito.internal.util.MockUtil;
import org.mockito.internal.verification.MockAwareVerificationMode;
import org.mockito.internal.verification.VerificationDataImpl;
import org.mockito.invocation.Invocation;
import org.mockito.invocation.InvocationContainer;
import org.mockito.invocation.MockHandler;
import org.mockito.mock.MockCreationSettings;
import org.mockito.verification.VerificationMode;

/**
 * Invocation handler set on mock objects.
 *
 * @param  type of mock object to handle
 */
public class MockHandlerImpl implements MockHandler {

    private static final long serialVersionUID = -2917871070982574165L;

    InvocationContainerImpl invocationContainer;

    MatchersBinder matchersBinder = new MatchersBinder();

    private final MockCreationSettings mockSettings;

    public MockHandlerImpl(MockCreationSettings mockSettings) {
        this.mockSettings = mockSettings;

        this.matchersBinder = new MatchersBinder();
        this.invocationContainer = new InvocationContainerImpl(mockSettings);
    }

    @Override
    public Object handle(Invocation invocation) throws Throwable {
        if (invocationContainer.hasAnswersForStubbing()) {
            // stubbing voids with doThrow() or doAnswer() style
            InvocationMatcher invocationMatcher =
                    matchersBinder.bindMatchers(
                            mockingProgress().getArgumentMatcherStorage(), invocation);
            invocationContainer.setMethodForStubbing(invocationMatcher);
            return null;
        }
        VerificationMode verificationMode = mockingProgress().pullVerificationMode();

        InvocationMatcher invocationMatcher =
                matchersBinder.bindMatchers(
                        mockingProgress().getArgumentMatcherStorage(), invocation);

        mockingProgress().validateState();

        // if verificationMode is not null then someone is doing verify()
        if (verificationMode != null) {
            // We need to check if verification was started on the correct mock
            // - see VerifyingWithAnExtraCallToADifferentMockTest (bug 138)
            if (MockUtil.areSameMocks(
                    ((MockAwareVerificationMode) verificationMode).getMock(),
                    invocation.getMock())) {
                VerificationDataImpl data =
                        new VerificationDataImpl(invocationContainer, invocationMatcher);
                verificationMode.verify(data);
                return null;
            } else {
                // this means there is an invocation on a different mock. Re-adding verification
                // mode
                // - see VerifyingWithAnExtraCallToADifferentMockTest (bug 138)
                mockingProgress().verificationStarted(verificationMode);
            }
        }

        // prepare invocation for stubbing
        invocationContainer.setInvocationForPotentialStubbing(invocationMatcher);
        OngoingStubbingImpl ongoingStubbing = new OngoingStubbingImpl(invocationContainer);
        mockingProgress().reportOngoingStubbing(ongoingStubbing);

        // look for existing answer for this invocation
        StubbedInvocationMatcher stubbing = invocationContainer.findAnswerFor(invocation);
        // TODO #793 - when completed, we should be able to get rid of the casting below
        notifyStubbedAnswerLookup(
                invocation,
                stubbing,
                invocationContainer.getStubbingsAscending(),
                (CreationSettings) mockSettings);

        if (stubbing != null) {
            stubbing.captureArgumentsFrom(invocation);

            try {
                return stubbing.answer(invocation);
            } finally {
                // Needed so that we correctly isolate stubbings in some scenarios
                // see MockitoStubbedCallInAnswerTest or issue #1279
                mockingProgress().reportOngoingStubbing(ongoingStubbing);
            }
        } else {
            try {
                Object ret = mockSettings.getDefaultAnswer().answer(invocation);
                DefaultAnswerValidator.validateReturnValueFor(invocation, ret);

                return ret;
            } finally {
                // Mockito uses it to redo setting invocation for potential stubbing in case of
                // partial
                // mocks / spies.
                // Without it, the real method inside 'when' might have delegated to other self
                // method
                // and overwrite the intended stubbed method with a different one.
                // This means we would be stubbing a wrong method.
                // Typically this would led to runtime exception that validates return type with
                // stubbed
                // method signature.
                invocationContainer.resetInvocationForPotentialStubbing(invocationMatcher);
                mockingProgress().reportOngoingStubbing(ongoingStubbing);
            }
        }
    }

    @Override
    public MockCreationSettings getMockSettings() {
        return mockSettings;
    }

    @Override
    public InvocationContainer getInvocationContainer() {
        return invocationContainer;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy