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