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.exceptions.Reporter; 8 import org.mockito.internal.InternalMockHandler; 9 import org.mockito.internal.invocation.InvocationMatcher; 10 import org.mockito.internal.invocation.MatchersBinder; 11 import org.mockito.internal.progress.MockingProgress; 12 import org.mockito.internal.progress.ThreadSafeMockingProgress; 13 import org.mockito.internal.stubbing.*; 14 import org.mockito.internal.verification.MockAwareVerificationMode; 15 import org.mockito.internal.verification.VerificationDataImpl; 16 import org.mockito.invocation.Invocation; 17 import org.mockito.mock.MockCreationSettings; 18 import org.mockito.stubbing.Answer; 19 import org.mockito.stubbing.VoidMethodStubbable; 20 import org.mockito.verification.VerificationMode; 21 22 import java.util.List; 23 24 /** 25 * Invocation handler set on mock objects. 26 * 27 * @param <T> 28 * type of mock object to handle 29 */ 30 class MockHandlerImpl<T> implements InternalMockHandler<T> { 31 32 private static final long serialVersionUID = -2917871070982574165L; 33 34 InvocationContainerImpl invocationContainerImpl; 35 MatchersBinder matchersBinder = new MatchersBinder(); 36 MockingProgress mockingProgress = new ThreadSafeMockingProgress(); 37 38 private final MockCreationSettings mockSettings; 39 40 public MockHandlerImpl(MockCreationSettings mockSettings) { 41 this.mockSettings = mockSettings; 42 this.mockingProgress = new ThreadSafeMockingProgress(); 43 this.matchersBinder = new MatchersBinder(); 44 this.invocationContainerImpl = new InvocationContainerImpl(mockingProgress, mockSettings); 45 } 46 47 public Object handle(Invocation invocation) throws Throwable { 48 if (invocationContainerImpl.hasAnswersForStubbing()) { 49 // stubbing voids with stubVoid() or doAnswer() style 50 InvocationMatcher invocationMatcher = matchersBinder.bindMatchers( 51 mockingProgress.getArgumentMatcherStorage(), 52 invocation 53 ); 54 invocationContainerImpl.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 = createVerificationData(invocationContainerImpl, 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 invocationContainerImpl.setInvocationForPotentialStubbing(invocationMatcher); 83 OngoingStubbingImpl<T> ongoingStubbing = new OngoingStubbingImpl<T>(invocationContainerImpl); 84 mockingProgress.reportOngoingStubbing(ongoingStubbing); 85 86 // look for existing answer for this invocation 87 StubbedInvocationMatcher stubbedInvocation = invocationContainerImpl.findAnswerFor(invocation); 88 89 if (stubbedInvocation != null) { 90 stubbedInvocation.captureArgumentsFrom(invocation); 91 return stubbedInvocation.answer(invocation); 92 } else { 93 Object ret = mockSettings.getDefaultAnswer().answer(invocation); 94 95 // redo setting invocation for potential stubbing in case of partial 96 // mocks / spies. 97 // Without it, the real method inside 'when' might have delegated 98 // to other self method and overwrite the intended stubbed method 99 // with a different one. The reset is required to avoid runtime exception that validates return type with stubbed method signature. 100 invocationContainerImpl.resetInvocationForPotentialStubbing(invocationMatcher); 101 return ret; 102 } 103 } 104 105 public VoidMethodStubbable<T> voidMethodStubbable(T mock) { 106 return new VoidMethodStubbableImpl<T>(mock, invocationContainerImpl); 107 } 108 109 public MockCreationSettings getMockSettings() { 110 return mockSettings; 111 } 112 113 @SuppressWarnings("unchecked") 114 public void setAnswersForStubbing(List<Answer> answers) { 115 invocationContainerImpl.setAnswersForStubbing(answers); 116 } 117 118 public InvocationContainer getInvocationContainer() { 119 return invocationContainerImpl; 120 } 121 122 private VerificationDataImpl createVerificationData(InvocationContainerImpl invocationContainerImpl, InvocationMatcher invocationMatcher) { 123 if (mockSettings.isStubOnly()) { 124 new Reporter().stubPassedToVerify(); // this throws an exception 125 } 126 127 return new VerificationDataImpl(invocationContainerImpl, invocationMatcher); 128 } 129 } 130 131