• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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