/** * Creates mock object of given class or interface. * <p> * See examples in javadoc for {@link Mockito} class * * @param classToMock class or interface to mock * @return mock object */ @CheckReturnValue publicstatic <T> T mock(Class<T> classToMock) { return mock(classToMock, withSettings()); }
接受一个 class 类型与一个 mockSettings,classToMock 就是我们需要 mock 对象的类型,而mockSettings 则记录着此次 mock 的一些信息。
public <T> T mock(Class<T> typeToMock, MockSettings settings) { if (!MockSettingsImpl.class.isInstance(settings)) { throw new IllegalArgumentException( "Unexpected implementation of '" + settings.getClass().getCanonicalName() + "'\n" + "At the moment, you cannot provide your own implementations of that class."); } MockSettingsImpl impl = MockSettingsImpl.class.cast(settings); MockCreationSettings<T> creationSettings = impl.build(typeToMock); // 创建 mock 对象 T mock = createMock(creationSettings); mockingProgress().mockingStarted(mock, creationSettings); return mock; }
Object doIntercept( Object mock, Method invokedMethod, Object[] arguments, RealMethod realMethod, Location location) throws Throwable { // If the currently dispatched method is used in a hot path, typically a tight loop and if // the mock is not used after the currently dispatched method, the JVM might attempt a // garbage collection of the mock instance even before the execution of the current // method is completed. Since we only reference the mock weakly from hereon after to avoid // leaking the instance, it might therefore be garbage collected before the // handler.handle(...) method completes. Since the handler method expects the mock to be // present while a method call onto the mock is dispatched, this can lead to the problem // described in GitHub #1802. // // To avoid this problem, we distract the JVM JIT by escaping the mock instance to a thread // local field for the duration of the handler's dispatch. // // When dropping support for Java 8, instead of this hatch we should use an explicit fence // https://docs.oracle.com/javase/9/docs/api/java/lang/ref/Reference.html#reachabilityFence-java.lang.Object- weakReferenceHatch.set(mock); try { return handler.handle( createInvocation( mock, invokedMethod, arguments, realMethod, mockCreationSettings, location)); } finally { weakReferenceHatch.remove(); } }
public Object handle(Invocation invocation)throws Throwable { // doAnswer 时会执行 if (invocationContainer.hasAnswersForStubbing()) { // stubbing voids with doThrow() or doAnswer() style InvocationMatcherinvocationMatcher= matchersBinder.bindMatchers( mockingProgress().getArgumentMatcherStorage(), invocation); invocationContainer.setMethodForStubbing(invocationMatcher); returnnull; } // VerificationMode 是对验证信息的封装,它是一个接口,含有 verify 函数, 例如常用的 never,times 返回的都是 Times 类型,而 Times 类型就是 VerificationMode 的一种实现。 然后,调用 mockingProgress 来缓存 mode 信息。 VerificationModeverificationMode= mockingProgress().pullVerificationMode(); // 用来匹配 when 条件 InvocationMatcherinvocationMatcher= matchersBinder.bindMatchers( mockingProgress().getArgumentMatcherStorage(), invocation);
mockingProgress().validateState();
// if verificationMode is not null then someone is doing verify() if (verificationMode != null) { // We need to check if verification was started on the correct mock // - see VerifyingWithAnExtraCallToADifferentMockTest (bug 138) if (((MockAwareVerificationMode) verificationMode).getMock() == invocation.getMock()) { VerificationDataImpldata= newVerificationDataImpl(invocationContainer, invocationMatcher); verificationMode.verify(data); returnnull; } else { // this means there is an invocation on a different mock. Re-adding verification // mode // - see VerifyingWithAnExtraCallToADifferentMockTest (bug 138) mockingProgress().verificationStarted(verificationMode); } }
// 寻找此 invocation 是否有 answer StubbedInvocationMatcherstubbing= invocationContainer.findAnswerFor(invocation); // TODO #793 - when completed, we should be able to get rid of the casting below notifyStubbedAnswerLookup( invocation, stubbing, invocationContainer.getStubbingsAscending(), (CreationSettings) mockSettings); // 如果有 answer 则进行 answer 的回调,上面的 例子中有,所以 case 执行会走到这里 if (stubbing != null) { stubbing.captureArgumentsFrom(invocation);
try { return stubbing.answer(invocation); } finally { // Needed so that we correctly isolate stubbings in some scenarios // see MockitoStubbedCallInAnswerTest or issue #1279 mockingProgress().reportOngoingStubbing(ongoingStubbing); } } else { Objectret= mockSettings.getDefaultAnswer().answer(invocation); DefaultAnswerValidator.validateReturnValueFor(invocation, ret);
// Mockito uses it to redo setting invocation for potential stubbing in case of partial // mocks / spies. // Without it, the real method inside 'when' might have delegated to other self method // and overwrite the intended stubbed method with a different one. // This means we would be stubbing a wrong method. // Typically this would led to runtime exception that validates return type with stubbed // method signature. invocationContainer.resetInvocationForPotentialStubbing(invocationMatcher); return ret; } }
这里说明以下,handle 方法在 when 中会被执行一次,再后面实际调用时还会执行一次,所以一次 when(method) 到实际 mock 调用,会执行两次。这里最开始在 debug 时,每次第一次走到都没符合预期返回 then 的值,后面通过分析堆栈发展调用入口时 when 中 mock 发起,并非时后面实际测试调用发起,才解释了这个没有预期返回结果的情况。
MockSettings has been introduced for two reasons. Firstly, to make it easy to add another mock setting when the demand comes. Secondly, to enable combining together different mock settings without introducing zillions of overloaded mock() methods.