Home | History | Annotate | Download | only in handler
      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