/* * Copyright (c) 2007 Mockito contributors * This program is made available under the terms of the MIT License. */ package org.mockito.internal.handler; import org.mockito.exceptions.Reporter; import org.mockito.internal.InternalMockHandler; import org.mockito.internal.invocation.InvocationMatcher; import org.mockito.internal.invocation.MatchersBinder; import org.mockito.internal.progress.MockingProgress; import org.mockito.internal.progress.ThreadSafeMockingProgress; import org.mockito.internal.stubbing.*; import org.mockito.internal.verification.MockAwareVerificationMode; import org.mockito.internal.verification.VerificationDataImpl; import org.mockito.invocation.Invocation; import org.mockito.mock.MockCreationSettings; import org.mockito.stubbing.Answer; import org.mockito.stubbing.VoidMethodStubbable; import org.mockito.verification.VerificationMode; import java.util.List; /** * Invocation handler set on mock objects. * * @param * type of mock object to handle */ class MockHandlerImpl implements InternalMockHandler { private static final long serialVersionUID = -2917871070982574165L; InvocationContainerImpl invocationContainerImpl; MatchersBinder matchersBinder = new MatchersBinder(); MockingProgress mockingProgress = new ThreadSafeMockingProgress(); private final MockCreationSettings mockSettings; public MockHandlerImpl(MockCreationSettings mockSettings) { this.mockSettings = mockSettings; this.mockingProgress = new ThreadSafeMockingProgress(); this.matchersBinder = new MatchersBinder(); this.invocationContainerImpl = new InvocationContainerImpl(mockingProgress, mockSettings); } public Object handle(Invocation invocation) throws Throwable { if (invocationContainerImpl.hasAnswersForStubbing()) { // stubbing voids with stubVoid() or doAnswer() style InvocationMatcher invocationMatcher = matchersBinder.bindMatchers( mockingProgress.getArgumentMatcherStorage(), invocation ); invocationContainerImpl.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 (((MockAwareVerificationMode) verificationMode).getMock() == invocation.getMock()) { VerificationDataImpl data = createVerificationData(invocationContainerImpl, 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 invocationContainerImpl.setInvocationForPotentialStubbing(invocationMatcher); OngoingStubbingImpl ongoingStubbing = new OngoingStubbingImpl(invocationContainerImpl); mockingProgress.reportOngoingStubbing(ongoingStubbing); // look for existing answer for this invocation StubbedInvocationMatcher stubbedInvocation = invocationContainerImpl.findAnswerFor(invocation); if (stubbedInvocation != null) { stubbedInvocation.captureArgumentsFrom(invocation); return stubbedInvocation.answer(invocation); } else { Object ret = mockSettings.getDefaultAnswer().answer(invocation); // 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. The reset is required to avoid runtime exception that validates return type with stubbed method signature. invocationContainerImpl.resetInvocationForPotentialStubbing(invocationMatcher); return ret; } } public VoidMethodStubbable voidMethodStubbable(T mock) { return new VoidMethodStubbableImpl(mock, invocationContainerImpl); } public MockCreationSettings getMockSettings() { return mockSettings; } @SuppressWarnings("unchecked") public void setAnswersForStubbing(List answers) { invocationContainerImpl.setAnswersForStubbing(answers); } public InvocationContainer getInvocationContainer() { return invocationContainerImpl; } private VerificationDataImpl createVerificationData(InvocationContainerImpl invocationContainerImpl, InvocationMatcher invocationMatcher) { if (mockSettings.isStubOnly()) { new Reporter().stubPassedToVerify(); // this throws an exception } return new VerificationDataImpl(invocationContainerImpl, invocationMatcher); } }