1/* 2 * Copyright (c) 2007 Mockito contributors 3 * This program is made available under the terms of the MIT License. 4 */ 5package org.mockito.internal.stubbing.defaultanswers; 6 7import org.mockito.MockSettings; 8import org.mockito.Mockito; 9import org.mockito.internal.InternalMockHandler; 10import org.mockito.internal.MockitoCore; 11import org.mockito.internal.creation.settings.CreationSettings; 12import org.mockito.internal.stubbing.InvocationContainerImpl; 13import org.mockito.internal.stubbing.StubbedInvocationMatcher; 14import org.mockito.internal.util.MockUtil; 15import org.mockito.internal.util.reflection.GenericMetadataSupport; 16import org.mockito.invocation.InvocationOnMock; 17import org.mockito.mock.MockCreationSettings; 18import org.mockito.stubbing.Answer; 19 20import java.io.IOException; 21import java.io.Serializable; 22 23import static org.mockito.Mockito.withSettings; 24 25/** 26 * Returning deep stub implementation. 27 * 28 * Will return previously created mock if the invocation matches. 29 * 30 * <p>Supports nested generic information, with this answer you can write code like this : 31 * 32 * <pre class="code"><code class="java"> 33 * interface GenericsNest<K extends Comparable<K> & Cloneable> extends Map<K, Set<Number>> {} 34 * 35 * GenericsNest<?> mock = mock(GenericsNest.class, new ReturnsGenericDeepStubs()); 36 * Number number = mock.entrySet().iterator().next().getValue().iterator().next(); 37 * </code></pre> 38 * </p> 39 * 40 * @see org.mockito.Mockito#RETURNS_DEEP_STUBS 41 * @see org.mockito.Answers#RETURNS_DEEP_STUBS 42 */ 43public class ReturnsDeepStubs implements Answer<Object>, Serializable { 44 45 private static final long serialVersionUID = -7105341425736035847L; 46 47 public Object answer(InvocationOnMock invocation) throws Throwable { 48 GenericMetadataSupport returnTypeGenericMetadata = 49 actualParameterizedType(invocation.getMock()).resolveGenericReturnType(invocation.getMethod()); 50 51 Class<?> rawType = returnTypeGenericMetadata.rawType(); 52 if (!mockitoCore().isTypeMockable(rawType)) { 53 return delegate().returnValueFor(rawType); 54 } 55 56 return deepStub(invocation, returnTypeGenericMetadata); 57 } 58 59 private Object deepStub(InvocationOnMock invocation, GenericMetadataSupport returnTypeGenericMetadata) throws Throwable { 60 InternalMockHandler<Object> handler = MockUtil.getMockHandler(invocation.getMock()); 61 InvocationContainerImpl container = (InvocationContainerImpl) handler.getInvocationContainer(); 62 63 // matches invocation for verification 64 for (StubbedInvocationMatcher stubbedInvocationMatcher : container.getStubbedInvocations()) { 65 if (container.getInvocationForStubbing().matches(stubbedInvocationMatcher.getInvocation())) { 66 return stubbedInvocationMatcher.answer(invocation); 67 } 68 } 69 70 // record deep stub answer 71 StubbedInvocationMatcher stubbing = recordDeepStubAnswer( 72 newDeepStubMock(returnTypeGenericMetadata, invocation.getMock()), 73 container 74 ); 75 76 // deep stubbing creates a stubbing and immediately uses it 77 // so the stubbing is actually used by the same invocation 78 stubbing.markStubUsed(stubbing.getInvocation()); 79 80 return stubbing.answer(invocation); 81 } 82 83 /** 84 * Creates a mock using the Generics Metadata. 85 * 86 * <li>Finally as we want to mock the actual type, but we want to pass along the contextual generics meta-data 87 * that was resolved for the current return type, for this to happen we associate to the mock an new instance of 88 * {@link ReturnsDeepStubs} answer in which we will store the returned type generic metadata. 89 * 90 * @param returnTypeGenericMetadata The metadata to use to create the new mock. 91 * @param parentMock The parent of the current deep stub mock. 92 * @return The mock 93 */ 94 private Object newDeepStubMock(GenericMetadataSupport returnTypeGenericMetadata, Object parentMock) { 95 MockCreationSettings parentMockSettings = MockUtil.getMockSettings(parentMock); 96 return mockitoCore().mock( 97 returnTypeGenericMetadata.rawType(), 98 withSettingsUsing(returnTypeGenericMetadata, parentMockSettings) 99 ); 100 } 101 102 private MockSettings withSettingsUsing(GenericMetadataSupport returnTypeGenericMetadata, MockCreationSettings parentMockSettings) { 103 MockSettings mockSettings = returnTypeGenericMetadata.hasRawExtraInterfaces() ? 104 withSettings().extraInterfaces(returnTypeGenericMetadata.rawExtraInterfaces()) 105 : withSettings(); 106 107 return propagateSerializationSettings(mockSettings, parentMockSettings) 108 .defaultAnswer(returnsDeepStubsAnswerUsing(returnTypeGenericMetadata)); 109 } 110 111 private MockSettings propagateSerializationSettings(MockSettings mockSettings, MockCreationSettings parentMockSettings) { 112 return mockSettings.serializable(parentMockSettings.getSerializableMode()); 113 } 114 115 private ReturnsDeepStubs returnsDeepStubsAnswerUsing(final GenericMetadataSupport returnTypeGenericMetadata) { 116 return new ReturnsDeepStubsSerializationFallback(returnTypeGenericMetadata); 117 } 118 119 private StubbedInvocationMatcher recordDeepStubAnswer(final Object mock, InvocationContainerImpl container) { 120 DeeplyStubbedAnswer answer = new DeeplyStubbedAnswer(mock); 121 return container.addAnswer(answer, false); 122 } 123 124 protected GenericMetadataSupport actualParameterizedType(Object mock) { 125 CreationSettings mockSettings = (CreationSettings) MockUtil.getMockHandler(mock).getMockSettings(); 126 return GenericMetadataSupport.inferFrom(mockSettings.getTypeToMock()); 127 } 128 129 130 private static class ReturnsDeepStubsSerializationFallback extends ReturnsDeepStubs implements Serializable { 131 @SuppressWarnings("serial") // not gonna be serialized 132 private final GenericMetadataSupport returnTypeGenericMetadata; 133 134 public ReturnsDeepStubsSerializationFallback(GenericMetadataSupport returnTypeGenericMetadata) { 135 this.returnTypeGenericMetadata = returnTypeGenericMetadata; 136 } 137 138 @Override 139 protected GenericMetadataSupport actualParameterizedType(Object mock) { 140 return returnTypeGenericMetadata; 141 } 142 private Object writeReplace() throws IOException { 143 return Mockito.RETURNS_DEEP_STUBS; 144 } 145 } 146 147 148 private static class DeeplyStubbedAnswer implements Answer<Object>, Serializable { 149 @SuppressWarnings("serial") // serialization will fail with a nice message if mock not serializable 150 private final Object mock; 151 152 DeeplyStubbedAnswer(Object mock) { 153 this.mock = mock; 154 } 155 public Object answer(InvocationOnMock invocation) throws Throwable { 156 return mock; 157 } 158 } 159 160 161 private static MockitoCore mockitoCore() { 162 return LazyHolder.MOCKITO_CORE; 163 } 164 165 private static ReturnsEmptyValues delegate() { 166 return LazyHolder.DELEGATE; 167 } 168 169 private static class LazyHolder { 170 private static final MockitoCore MOCKITO_CORE = new MockitoCore(); 171 private static final ReturnsEmptyValues DELEGATE = new ReturnsEmptyValues(); 172 } 173} 174