1 /* 2 * Copyright (c) 2007 Mockito contributors 3 * This program is made available under the terms of the MIT License. 4 */ 5 package org.mockito.internal.handler; 6 7 import org.mockito.internal.creation.settings.CreationSettings; 8 import org.mockito.internal.invocation.InvocationMatcher; 9 import org.mockito.internal.invocation.MatchersBinder; 10 import org.mockito.internal.stubbing.InvocationContainerImpl; 11 import org.mockito.internal.stubbing.OngoingStubbingImpl; 12 import org.mockito.internal.stubbing.StubbedInvocationMatcher; 13 import org.mockito.internal.stubbing.answers.DefaultAnswerValidator; 14 import org.mockito.internal.verification.MockAwareVerificationMode; 15 import org.mockito.internal.verification.VerificationDataImpl; 16 import org.mockito.invocation.Invocation; 17 import org.mockito.invocation.InvocationContainer; 18 import org.mockito.invocation.MockHandler; 19 import org.mockito.mock.MockCreationSettings; 20 import org.mockito.verification.VerificationMode; 21 22 import static org.mockito.internal.listeners.StubbingLookupNotifier.notifyStubbedAnswerLookup; 23 import static org.mockito.internal.progress.ThreadSafeMockingProgress.mockingProgress; 24 25 /** 26 * Invocation handler set on mock objects. 27 * 28 * @param <T> type of mock object to handle 29 */ 30 public class MockHandlerImpl<T> implements MockHandler<T> { 31 32 private static final long serialVersionUID = -2917871070982574165L; 33 34 InvocationContainerImpl invocationContainer; 35 36 MatchersBinder matchersBinder = new MatchersBinder(); 37 38 private final MockCreationSettings<T> mockSettings; 39 MockHandlerImpl(MockCreationSettings<T> mockSettings)40 public MockHandlerImpl(MockCreationSettings<T> mockSettings) { 41 this.mockSettings = mockSettings; 42 43 this.matchersBinder = new MatchersBinder(); 44 this.invocationContainer = new InvocationContainerImpl(mockSettings); 45 } 46 handle(Invocation invocation)47 public Object handle(Invocation invocation) throws Throwable { 48 if (invocationContainer.hasAnswersForStubbing()) { 49 // stubbing voids with doThrow() or doAnswer() style 50 InvocationMatcher invocationMatcher = matchersBinder.bindMatchers( 51 mockingProgress().getArgumentMatcherStorage(), 52 invocation 53 ); 54 invocationContainer.setMethodForStubbing(invocationMatcher); 55 return null; 56 } 57 VerificationMode verificationMode = mockingProgress().pullVerificationMode(); 58 59 InvocationMatcher invocationMatcher = matchersBinder.bindMatchers( 60 mockingProgress().getArgumentMatcherStorage(), 61 invocation 62 ); 63 64 mockingProgress().validateState(); 65 66 // if verificationMode is not null then someone is doing verify() 67 if (verificationMode != null) { 68 // We need to check if verification was started on the correct mock 69 // - see VerifyingWithAnExtraCallToADifferentMockTest (bug 138) 70 if (((MockAwareVerificationMode) verificationMode).getMock() == invocation.getMock()) { 71 VerificationDataImpl data = new VerificationDataImpl(invocationContainer, invocationMatcher); 72 verificationMode.verify(data); 73 return null; 74 } else { 75 // this means there is an invocation on a different mock. Re-adding verification mode 76 // - see VerifyingWithAnExtraCallToADifferentMockTest (bug 138) 77 mockingProgress().verificationStarted(verificationMode); 78 } 79 } 80 81 // prepare invocation for stubbing 82 invocationContainer.setInvocationForPotentialStubbing(invocationMatcher); 83 OngoingStubbingImpl<T> ongoingStubbing = new OngoingStubbingImpl<T>(invocationContainer); 84 mockingProgress().reportOngoingStubbing(ongoingStubbing); 85 86 // look for existing answer for this invocation 87 StubbedInvocationMatcher stubbing = invocationContainer.findAnswerFor(invocation); 88 // TODO #793 - when completed, we should be able to get rid of the casting below 89 notifyStubbedAnswerLookup(invocation, stubbing, invocationContainer.getStubbingsAscending(), 90 (CreationSettings) mockSettings); 91 92 if (stubbing != null) { 93 stubbing.captureArgumentsFrom(invocation); 94 95 try { 96 return stubbing.answer(invocation); 97 } finally { 98 //Needed so that we correctly isolate stubbings in some scenarios 99 //see MockitoStubbedCallInAnswerTest or issue #1279 100 mockingProgress().reportOngoingStubbing(ongoingStubbing); 101 } 102 } else { 103 Object ret = mockSettings.getDefaultAnswer().answer(invocation); 104 DefaultAnswerValidator.validateReturnValueFor(invocation, ret); 105 106 //Mockito uses it to redo setting invocation for potential stubbing in case of partial mocks / spies. 107 //Without it, the real method inside 'when' might have delegated to other self method 108 //and overwrite the intended stubbed method with a different one. 109 //This means we would be stubbing a wrong method. 110 //Typically this would led to runtime exception that validates return type with stubbed method signature. 111 invocationContainer.resetInvocationForPotentialStubbing(invocationMatcher); 112 return ret; 113 } 114 } 115 getMockSettings()116 public MockCreationSettings<T> getMockSettings() { 117 return mockSettings; 118 } 119 getInvocationContainer()120 public InvocationContainer getInvocationContainer() { 121 return invocationContainer; 122 } 123 } 124